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


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.


Well i must say, so far i am impressed with ColdFusion 10. They have really done a large overhaul to its base along with many new features. One thing i am impressed with is that adobe has replaced the older antiquated JRun subsystem that CF use to run on with Tomcat. Tomcat is notably more enhanced then JRun when it comes to an application built on Java. The draw back is that the structure of the new CF is very different from the older versions, especially when it comes to multi instance applications.

Adobe has tried to keep things as similar as possible, but one thing i noticed is that when installing a multi instance server, you no longer have that option on the initial install. You will have the option to install CF 10 initially, then if your license supports multi instance, you will then have the option afterwards to install another instance. Now onto the part that i have been heavily interested in.

One thing that was an enormous draw back on earlier versions of CF was that when you installed multiple instances for separate applications, you had 1 JVM config file that was shared between all instances. Now what does this mean?. Well it means that any memory adjustments or class file inclusions you made, applied to all instances. So this was not truly segmented if they all shared 1 file. I always believed that multi instance applications should be completely autonomous, and nothing should affect the other instances. Else this really defeats the purpose of having separate instances. So on we go, in older versions it was not impossible to have separate JVM config files for each instance, it was just a pain in the rear to remove the service and re install it while assigning the service to a new JVM config. CF 10 has inherited this idea and has truly segmented instances from one another. It was very confusing to me because i was use to all instances sharing the same bin and lib folders in the root. Well in CF10 each instance is listed in the root folder of CF10 as it’s own folder and under that having it’s own JVM and file structure. This makes each instance totally autonomous. Awesome!.

Another advancement has been to their schedule task manager. Anyone who know me, knows this drove me nuts. Previously when tasks failed, you would never know unless you trapped for the failure in your task. And the logging for these tasks were vague at best. Well they have completely redone this aspect and given Schedule Task Manager a true face lift with some pretty cool features. So i encourage all you CF geeks out there, give the below link a look and leave your comments on what you think of the new and improved version of CF. Happy coding!!.

http://labs.adobe.com/wiki/index.php/ColdFusion_10#Documentation

 


Here we are again with another ColdFusion lesson learned. The thought that came to my mind when i was working with a CFLayout tab, was how do i refresh the tab’s contents other then using the standard ways of submitting a form.

In this case i had a button that would make an Ajax call to process a selection Asynchronously. After that call i was looking to see how we can display those changes on the tab using Javascript. Well lucky us, ColdFusion Layout objects are accessible using JavaScript. Here is an example:

   <script type="text/javascript">
      ColdFusion.Layout.hideTab('mainLayout', 'userPerm');
      ColdFusion.Layout.showTab('mainLayout', 'userPerm');
      ColdFusion.Layout.selectTab('mainLayout', 'userPerm');
  </script>

What we are doing here is pretty self explanatory. We are hiding the tab first which basicly, removes the tab from the layout. Then we show the tab in the layout which does a call to refresh the contents of the tab. This is the same as doing an Ajax call to get contents of a page except we are using ColdFusion to do it. Then we select the tab in the last step which brings us back to where we started.

Now i will warn you that my experience with this method is flaky at best. The method i found to work without doing a form submission or doing a new pageload, was to do the Ajax call myself instead of relying on ColdFusion.

Now i know it is not easy to run JavaScript that is part of a tab contents, but if you know what calls are needed then you can have your JavaScript functions included on the parent page, which gives you the ability to call those functions within the tabs. What this does is allow you to wrap your tab HTML in a div, then call Ajax to retrieve the contents again, and set the innerHTML of the div equal to the new content. All this done without a whole pageload. Now i am certain there are many ways to skin this cat, but you will see i am a fan of Ajax. I like the experience of processing data in the background which provides clean HTML reloads. If anyone would like to add to this, or critique the method, by all means let me know. I am always open to learning something new!.

%d bloggers like this: