Tag Archive: coldfusion



I thought I would write a little about this because anyone who knows me knows I am not a big fan of ColdFusion’s task manager. I think CF lacks hugely in this area, and although they have made some enormous moves forward with the task manager in 10, there are some key areas that they still lack in.

First major area, is logging and alerting. Before 10 when tasks failed, you would get a generic log entry in the admin logs and then nothing else would get done. So for those who depend on their tasks to run, it is kinda nice to know when it doesn’t. Even better, when the task fails it is nice to know where in the script the task failed so that if you are in a cfloop you know what iteration it was on when it failed. Trust me knowing the iteration of a failed task helps reduce time you spend tracking down errors in a task. Many times the problem is just in the data, not the script. So if you are trying to find why your task failed but have no detailed information, you may spend hours tracking down a task to find out that the data that it was looping over in a query was bad.

So I have always been a big fan of writing your own. Task manager. Now I know it sounds like a big job, and in part it is. But when it is done and you see the benefit, trust me it is worth the investment in time. Now the tedious part is getting all the moving parts together. Writing a task manager in CF is not hard, you just need to make sure it has all the bells and whistles you want.

All CF essentially does in the scheduled task manager is a http get on the URL you give the manager, at the interval you tell it to run that URL. That is the basics, so pretty easy right?. This is very easy to replicate in that all you need to do is build an app that stores its tasks in a database. Then write a script that queries those tasks and gets a list together of everything it needs to do an http get for. The script will loop through the query and call the url’s it needs to then exit. This means that all you need to do is setup 1 script that runs the http gets and put that script in the CF task manager.

I call my script ever 2 mins from the CF admin scheduled task area. So when CF runs the script it looks for all tasks that need to run within the next 2 mins. The next step I did was I wrote a custom tag that I use to wrap around my tasks that my CF script is doing a GET on. What this custom tag does is keep track of when it is called by your STM and weather it finished. What I also did was write a second custom tag that logs a custom message based on what you pass in. You can call this custom tag at any area you put it in your task. This allows you to see that if the task fails, you will know what step it was on when it failed.

Now putting together the script that runs all the tasks as I mentioned before can be a bit convoluted, especially If you are trying to replicate all the CF task manager offers plus more. One thing I added on was the ability to run tasks a a set number of days. This means if you want to run a task every 10 days you can. I had also added in grouping before CF 10 came out, because it fit into our business model but is not necessary.

Now the script that runs all the tasks is just that, a script. You will then need to write an app that gives the users the ability to administer tasks. Another benefit to all this, is that if you have a lot of web servers for different environments, like I do, this manager centralizes all your tasks and gives you the ability to customize your logging to fit your needs. I also run a task every morning that just goes out and checks how many tasks failed the previous day, and only sends an email to the appropriate people if there is a task that has failed.

I hope this post has shed a little light for you and reaches someone that can put this to use. Feel free to let me know if you have any specific questions on how this is all put together. Or specific script questions. I will be happy to help.

Advertisements

A reader asked, “I want a tab to show a list of orders to approve. When the user selects an order to approve I want to approve the order selected, then refresh the list under the tab”.

So this one is for you. One way i gave before was to use the coldfusion javascript tags to deselect then reselect the tab which would refresh the content, as long as you had refreshonactivate set to true for the cflayoutarea. Now one thing i had mentioned before was that using the functions were flaky at best. So another method i used is the following.

AjaxEngine.js

var http;
var textResponse;
var returnFunction = "httpReturnResults";

function sendHttpRequest(params) {
// Create the HTTP Object
http = new getHTTPObject();

if (!isWorking && http) {
 if (params != "") {
 httpURL = url + "?nocounter=yes" + "&" + encodeURI(params); + "hash=" + Math.random();

http.open("POST", httpURL, true);
 http.setRequestHeader('Content-Type','application/x-www-form-urlencoded');
 http.setRequestHeader("If-Modified-Since", "Sat, 1 Jan 2000 00:00:00 GMT");
 http.onreadystatechange = handleTextHttpResponse;
 if (arguments[1]) {
 returnFunction = arguments[1];
 }
 isWorking = true;
 http.send(params);
 }
 }
}

function handleTextHttpResponse() {
results = "";
if (http.readyState == 4) {
 results = http.responseText;
 eval(returnFunction+"(results)");
 isWorking = false;
 }
}

function handleXmlHttpResponse() {
if (http.readyState == 4) {
 // XML Parsing
 xStr = "";
 xArray = new Array();
 var xmlDoc = http.responseXML;
 var xRoot = xmlDoc.getElementsByTagName('sources');
 for (x=0; x < xRoot.length; x++) {
 for (xx=0; xx < xRoot[x].childNodes.length; xx++) {
 xNodeName_id = xRoot[x].childNodes[xx].firstChild.nodeName;
 xNodeName_val = xRoot[x].childNodes[xx].lastChild.nodeName;
 xNodeId = xRoot[x].getElementsByTagName(xNodeName_id).item(xx).firstChild.data;
 xNodeValue = xRoot[x].getElementsByTagName(xNodeName_val).item(xx).firstChild.data;
 xStr += xRoot[x].getElementsByTagName(xNodeName_val).item(xx).firstChild.data + "\n";
 xArray[xx] = xNodeId + "_" + xNodeValue;
 }
 }
 results = xStr;
 }
 httpReturnResults(results);
 isWorking = false;
}

var isWorking = false;

function getHTTPObject() {
var xmlhttp;
// NOT SURE WHY THIS HAS TO BE HERE, BUT THE FUNCTION WILL NOT WORK IN IE WITHOUT IT...DO NOT REMOVE
/*@cc_on
@if (@_jscript_version >= 5)
 try {
 xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
 }
 catch (e) {
 try {
 xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
 }
 catch (E) {
 xmlhttp = false;
 }
 }
@else
 xmlhttp = false;
@end @*/

if (!xmlhttp && typeof XMLHttpRequest != 'undefined') {
 try {
 xmlhttp = new XMLHttpRequest();
 //xmlhttp.overrideMimeType("text/xml"); //this is for XML files only
 }
 catch (e) {
 xmlhttp = false;
 }
 }

if (!xmlhttp && window.createRequest) {
 try {
 xmlhttp = window.createRequest();
 } catch (e) {
 xmlhttp=false;
 }
}

return xmlhttp;
}

Index.cfm

<script type="text/javascript">
        <cfinclude template="ajaxEngine.js">
     function conReturn(obj){
         document.getElementById('divid').innerHTML = obj;
     }
     function senddata(){
          url = "approveorder.cfm";
          paramStr = 'urlparam=1';
          sendHttpRequest(paramStr,'conReturn');
     }
</script>

<cflayout type="tab" align="center" name="mainLayout" tabheight="620px">
      <cflayoutarea title="View Some Title" refreshonactivate="true" name="test2" source="test2.cfm">
      </cflayoutarea>
</cflayout>

Test2.cfm

     <div id="divid">
          <!---- content here ----->
          <input type="button" name="approveorder" id="approveorder" value="Approve Order" onClick="senddata();">
     </div>

approveorder.cfm

      <!----- once approve order button is clicked it will pass data to this page and then take the html that was produced here and replace divid with this content so you should make sure that the output of this page looks exactly like test2.cfm just does all the grabbing of updates and any other processing you want----->
      <!---- content here ----->
          <input type="button" name="approveorder" id="approveorder" value="Approve Order" onClick="senddata();">

After testing this a bit and trying out some of the javascript functions that are available from ColdFusion when the layout is included, i found this method to be more reliable to ensure the events fire correctly. I am sure there is a cleaner way to do this, so i invite any comments on your experience of making this happen.


A good friend of mine today showed me that you can teach an old dog new tricks. When you have been in the game a while you would like to think that you know just about all there is to know about a programming language. Then someone comes along and shows you something that is amazing. How did I not know this?. The two things you can do is 1 deflect and act like you knew it all along or 2 you can accept the fact that someone out there might have a good idea that is useful and admit you don’t know everything. Well i digress, the point of this was to teach all of you that no matter how long you have been in the game, be open to learning something from someone, it is ok. Trust me they won’t think you are an idiot for not knowing, they may think better of you for admitting you don’t know everything.

So on to the point. What I learned today was that using CFHTTP which is the equlivalent of opening your browser and going to a URL, you can invoke a CFC method as long as the access=”remote” in your cffunction

EX:


<cfhttp url="http://192.168.0.0/test/test.cfc?wsdl" method="post">
   <cfhttpparam name="method" value="methodname" type="formfield">
   <cfhttpparam name="methodarg" value="#arg1#" type="formfield">
</cfhttp>

I am looking at this example amazed. Understnding the potential security implications this can have. Essentially anyone can open a web browser and pass the method name with the arguments to a CFC in your directory and this will execute your CFC from an unknown user.

ColdFusion has made it so easy for developers to implement code and make remote calls, that they opened up a can of worms that could lead to serious issues. These days many hackers have the patience to figure out the holes in our systems. It is our job to ensure that they are not the ones that test out and find exploits in our code. This means closing the gaps. Be thorough not complacent with making sure that your application from top to bottom is balanced between being secure and yet very easy to use. This proves to be a very valuable lesson. Be very careful with allowing remote access to your CFC’s. If you need to allow remote access, make sure you validate your data and authenticate it before allowing it to be executed.

I would like to open up the lines and ask for comments on this. Let me know your experiences with this.


Back in January of last year i wrote a post on how to write a query on how to write a MS SQL query that would output the ID of the row that you are deleting without having to write 2 seperate queries. Now this does not only work on delete, but it also works on updates and inserts.

Before SQL 2005 you would need to use @@IDENTITY,SCOPE_IDENTITY, or IDENT_CURRENT to select out your inserted ID. The problem that can plague some of these is when you have several queries on the same page that are performing this same operation, you can have adverse results.

From SQL 2005 to current you can now use OUTPUT on your statement to output a Deleted, Updated, or Inserted id without running a seperate query before the action to determine the ID. I have seen many developers fall prey to running 2 queries to determine the ID being changed and then changing it. The problem with this is that you force your application to prepare and create 2 seperate threads for a transaction that you can do with 1 statement. We all like to trim our code to be efficient, so SQL has helped with this by allowing you to OUTPUT your modified rows id in the same statement as the one doing the modifications.

So a few days ago i thought, well I know how to do that but what if I wanted to insert a row in 1 table and then use that id to update a row in another table. Not only how to do that but how do you do all that within 1 statement so as to keep my ColdFusion application from having to prepare and send 2 separate statements. so here is the example.


 <cfquery name="qinsertRec" datasource="#datasource#">
	DECLARE @retId TABLE(id int);

        INSERT INTO testtable1
		(source,
		 dest,
		 TransDate,
		 username,
		 backupKey,
		 filename,
		 [action])
	OUTPUT INSERTED.id INTO @retId
	VALUES
		(<cfqueryparam cfsqltype="cf_sql_varchar" value="#copyFrom#">,
		 <cfqueryparam cfsqltype="cf_sql_varchar" value="#copyto#">,
		 <cfqueryparam cfsqltype="cf_sql_date" value="#dateTime#">,
		 <cfqueryparam cfsqltype="cf_sql_integer" value="1234">,
		 <cfqueryparam cfsqltype="cf_sql_bigint" value="14">,
		 <cfqueryparam cfsqltype="cf_sql_varchar" value="test.txt">,
		 <cfqueryparam cfsqltype="cf_sql_varchar" value="Restore">);

	UPDATE testtable2
	SET    trans_id_rest = (SELECT id
	                        FROM @retId),
	       status = 'Restored'
	WHERE  id = <cfqueryparam cfsqltype="cf_sql_integer" value="14">;
 </cfquery>

DECLARE

First we declare a temp table to output our ID into.

INSERT

Then we run our insert statement, remember to put your output between your column definitions and your values. Now instead of just writing OUTPUT INSERTED.id, we are going to put OUTPUT INSERTED.id INTO @retId. This will insert the id into our temp table so that we can select it back out when we run our UPDATE statement. Of course always remember to use your cfqueryparam’s to prevent for injections of people that want to do malicious things to your site.

UPDATE

Now we can do our update to the other table and select out the id from the declared temp table.

%d bloggers like this: