Programmatically Deleting Scheduled Tasks In ColdFusion

Posted October 18, 2006 at 3:34 PM by Ben Nadel

Tags: ColdFusion

Inspired by Mike Schierberl's use of Java to programmatically delete ColdFusion scheduled tasks, I decided to try and to accomplish the same thing with plain old ColdFusion. I have tried to do this in the past, but always failed (and ended up creating a phantom task on the server - yikes!). I would have never been able to do this without Mike's absolutely critical explanation:

The Adobe techNote is somewhat misleading, it turns out that delete does work through the cfschedule tag, but the problem arises when the scheduled task tries to delete itself.

That is a really, really important note indeed. But, I would like to take that one step further: A scheduled task cannot be deleted while it is still running. Well, at least not programmatically I think.

So, the trick here, is to be able to get a scheduled task to call another page that then deletes the scheduled task, AND this has to be done only after the scheduled task has stopped running. Sounds complicated? It's actually very simple to do and it all comes down to page timeouts.

Let's take a quick look at the code. Here is the page getting called by the scheduled task:

  • <!--- Make sure the page can only run for 5 seconds. --->
  • <cfsetting
  • requesttimeout="5"
  • />
  •  
  •  
  • <!--- Get this directory. --->
  • <cfset strDirectoryUrl = GetDirectoryFromPath(
  • "http://#CGI.server_name##CGI.script_name#"
  • ) />
  •  
  • <!---
  • Call execute task page. This is the page that will delete
  • the currently running task. Put in a CFTry / CFCatch blocks
  • because you never know how CFHttp might mess up.
  •  
  • Tell the CFHttp tag to only allow ONE second of timeout
  • time before it gives up waiting and continues on with the
  • page processing.
  • --->
  • <cftry>
  •  
  • <cfhttp
  • url="#strDirectoryUrl#execute_task.cfm"
  • method="GET"
  • useragent="Mozilla/5.0"
  • timeout="1"
  • />
  •  
  • <cfcatch>
  • <!--- An error occurred. --->
  • </cfcatch>
  • </cftry>
  •  
  •  
  • <!---
  • Trace the execution to a file just so we can make sure we
  • know exactly when the task is running.
  • --->
  • <cffile
  • action="APPEND"
  • file="#ExpandPath( './trace.txt' )#"
  • output="BT2 - Executed: #TimeFormat( Now(), 'hh:mm:ss:l' )#
  • addnewline="true"
  • />

Now, let's take a look at the execute task page that deletes the scheduled task:

  • <!---
  • Get the currently executing thread and force it
  • to sleep for 10 seconds.
  • --->
  • <cfset CreateObject(
  • "java",
  • "java.lang.Thread"
  • ).CurrentThread().Sleep(
  • JavaCast( "long", 10000 )
  • ) />
  •  
  •  
  • <!--- Kill task. --->
  • <cfschedule
  • action="DELETE"
  • task="BenTestTask2"
  • />

This all works quite nicely. And, as I said before, it's all thanks to page timeouts.

Take a look at the execute task page. Notice that the first thing the page does is sleep (thanks Mark Mandel's code example for asyncHttp) for 10 seconds. Then, after sleeping, it deletes the task. Now, look at the original task page. Notice that it only allows 5 seconds for page execution and the MINIMAL amount of timeout for the CFHttp request (one second) to wait for a response. This means that the calling page (the original scheduled task) should either finish executing OR timeout in some way before the Sleep() on the target page ever finishes. The bottom line: we can be sure that by the time the CFSchedule tag is executing, the original page has is NO LONGER RUNNING.




Reader Comments

Oct 18, 2006 at 4:17 PM // reply »
1 Comments

Nice work Ben, nice to finally understand what the root of the problem was. Hopefully this will be fixed in Scorpio (although I don't have my hopes up considering they knew about the issue in CF6)

Couple of things I would add...

1) may want to consider adding throwonerror=no to the cfhttp tag. You won't see the error because it is in a scheduled task, but I think it would throw an exception every time

2) may want to think about implementing a lock to make sure that the scheduled task isn't running. Think of a scenario with a scheduled task running every 60 seconds. If your first request takes 58 seconds to process, it may start a new thread 2 seconds later. At this point the delete task is waiting 10 seconds, and when it tries to delete, the task will be running again. Unlikely, but it may cause some strange behavior.

Nice work!


Oct 19, 2006 at 7:21 AM // reply »
11,246 Comments

Mike,

For starters, never could have done without your dynamite insights first. As for the throwonerror, I think it defaults to not throwing an error, however, it would be nice to see it explicitly written out.

As for the lock... I follow, that, would be cool. I figure I could put a NAMED lock on the entire scheduled task, then put a lock on th execute_taks.cfm page as well.... hmmm, but then one could lock the other and vice versa. I will play around.


jM
Nov 4, 2006 at 2:22 PM // reply »
9 Comments

Ben,

Very helpful post. Bit thanks to both you and Mike. Did you ever have time to investigate locking ideas?


Nov 5, 2006 at 10:56 AM // reply »
11,246 Comments

I have not looked into the locking issue yet, but I have an idea for something even better. Hopefully I can post soon.


jM
Nov 8, 2006 at 12:47 AM // reply »
9 Comments

I'll stay tuned.. I look forward to reading about your new idea.


Nov 8, 2006 at 7:25 AM // reply »
11,246 Comments

JM,

My new idea didn't work. I thought maybe I could do a page transfer (GetPageContext().Transfer()) to the page that would be deleting the task, but it did not work. The Transfer() method should kill the current page request, but I guess from ColdFusion's standpoint it's still the same thread or something. I don't know enough about what is going on underneath to understand why any of it doesn't work.

Sorry.


jM
Nov 9, 2006 at 6:06 PM // reply »
9 Comments

Ben,

This may be a dumb question but what is getPageContext().Transfer()? I only know about getPageContext().forward() and include(). Is transfer() like ASP's Server.Transfer()?


jM
Nov 9, 2006 at 6:09 PM // reply »
9 Comments

Ben,

I forgot to say thanks for the followup. I appreciate it.


Nov 10, 2006 at 7:21 AM // reply »
11,246 Comments

JM,

Sorry, that was my mistake. I did mean GetPageContext().Forward(). I don't know what Transfer is... i think I had something else on my mind at the time.


jM
Nov 13, 2006 at 5:08 AM // reply »
9 Comments

Ben,

No problem. You know I was reading Mike's blog entry and got to wondering about his comment: what happens if the scheduled task continues processing after the asynch http call.

I ran a few tests using CurrentThread().sleep() to force the scheduled task to continue executing for 30 seconds after the http call. The results I observed were that it created a phantom task.

Not that surprising I guess but it got me thinking about adobe's tech note 18361. Can you really delete a task through the CF Administrator if the task is still executing? So I created a recurring task that ran every 2 minutes. Then tried deleting the task in the ColdFusion Administrator (while the task was executing). The results were the same. It created a phantom task.

I'm curious if anyone else sees the same results or flaws in the test logic.


Nov 13, 2006 at 7:15 AM // reply »
11,246 Comments

jM,

That's very interesting. I guess a task cannot be deleted through any method so long as the task is still running. Doesn't that seem strange? You would think that the thread running it would be totally separate from the definition of it... why should the two overlap?


jM
Nov 16, 2006 at 12:27 AM // reply »
9 Comments

Ben,

I ran some more tests. Stranger still is that "updates" seem to have no effect either, if you apply the changes while the task is running. I'm wondering if the thread executing the task is blocking changes or maybe the updating thread isn't communicating the changes to the running task. Without knowing about the internal workings this is all just guesswork of course. But if my tests are correct then you cannot even modify a task through the CF Admin, if the task is currently running. Thats a bit disturbing..


Nov 16, 2006 at 7:17 AM // reply »
11,246 Comments

jM,

Nice testing! Yeah hopefully this is something that they can fix in the upcoming versions. It has been known about (to a degree) for some time now.


jM
Nov 16, 2006 at 9:00 PM // reply »
9 Comments

Ben,

Yes its definitely been around for a while. I am bit surprised that I haven't read more about the issue as it relates to any attempts to change a running task. The technote only mentions a problem with deletes. The neocron.xml file problem seems to have been fixed but the rogue task behavior still persists.

Like you said, hopefully it will get fixed in newer versions. It would also be nice to see some extended task information like "last run on date" or the "next scheduled date"... and of course a "kill" task feature might be nice ;)


Jun 11, 2007 at 8:04 PM // reply »
1 Comments

This blog has been very informative for me.

Carmelo Lisciotto


Nov 6, 2007 at 7:54 PM // reply »
34 Comments

You can also write a coldfusion template to edit the neo-cron.xml file and delete the scheduled tasks. This example:

http://www.coldfusionguy.com/ColdFusion/blog/index.cfm/2007/10/28/ColdFusion-Script-to-clean-expired-or-disabled-scheduled-tasks

Demonstrates how to delete expired and disabled scheduled tasks but you could modify it to add/edit/delete the scheduled tasks based on other factors as well.


Nov 7, 2007 at 8:58 AM // reply »
11,246 Comments

@Scott,

That is a cool idea. The only thing worries me about it is that the path the XML file is hard coded. Other than that, very cool.


Nov 7, 2007 at 5:09 PM // reply »
34 Comments

@Ben

that's a good point. I wouldn't normally hard code something like that either, but it was just a simple proof of concept for a blog entry, so I try to keep them as straightforward as possible. If I were really in the need for something like that, I would probably make a custom tag, or more likely these days a CFC, that could add, edit, and delete the scheduled tasks and I would use the #server.coldfusion.rootdir# to get the coldfusion root folder in my cffile tags. And now that I'm thinking about it I would put named cflocks around the process to protect the integrity of the file. Perhaps I should add a little note about that on my entry for people that don't yet know the potential risks with editing these kinds of files.


Nov 7, 2007 at 5:13 PM // reply »
11,246 Comments

@Scott,

Yeah, that sounds like a good solution. I forgot about the SERVER-scoped settings. I need to take another look at those to see what googies can be leveraged.


Nov 7, 2007 at 6:02 PM // reply »
34 Comments

There's not too much that's usefull, rootdir is about the only one I ever really use. here's all of the server variables as of CF8:

server.COLDFUSION.APPSERVER
server.COLDFUSION.EXPIRATION
server.COLDFUSION.INSTALLKIT
server.COLDFUSION.PRODUCTLEVEL
server.COLDFUSION.PRODUCTNAME
server.COLDFUSION.PRODUCTVERSION
server.COLDFUSION.ROOTDIR
server.COLDFUSION.SUPPORTEDLOCALES
server.OS.ADDITIONALINFORMATION
server.OS.ARCH
server.OS.BUILDNUMBER
server.OS.NAME
server.OS.VERSION



Post A Comment

Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.

Please review the following issues:

Author Name:


Author Email:

Author Website:

Comment:

Supported HTML tags for formatting: <strong>bold</strong>   <em>italic</em>   <code>code</code>







  • Help Wanted - Find Your Next ColdFusion Job
Ben Nadel's Company - Epicenter Consulting Recent Blog Comments
May 24, 2013 at 11:21 AM
Strange Interaction Between DeserializeJson(), ArrayContains(), And Database Values In ColdFusion
@WebManWalking, Ha ha, let's us never speak of justifying "##" notation again :P ... read »
May 24, 2013 at 11:18 AM
Strange Interaction Between DeserializeJson(), ArrayContains(), And Database Values In ColdFusion
@Ben, Ah, so it was indeed how I vaguely remembered it to be: A direct assignment value = users.id[ i ] causes value to retain the sticky datatype of the query column. Although unnecessary in ... read »
May 24, 2013 at 9:11 AM
Preventing Links In Standalone iPhone Applications From Opening In Mobile Safari
@Brandon, Hi, No, I haven't been able to do that. I have just kept it as it is. ... read »
May 23, 2013 at 9:52 PM
Preventing Links In Standalone iPhone Applications From Opening In Mobile Safari
@Muhmmadibn Did you figure out a solution to launching PDFs? I am running into the same issues myself. There is no way to close the PDF or go back once you launch it. Thanks in advance! ... read »
May 23, 2013 at 6:06 PM
The Girl Who Broke My Heart, And Made Me A Better Person
Good day,ladies and gentle men, my name is Dr AMADI the great spell caster in Africa, i have help so many people for different kind of problems,who say there is no solution to problems on earth, that ... read »
May 23, 2013 at 4:26 PM
ColdFusion QueryAppend( qOne, qTwo )
@Heather, Glad people are still getting value out of this! ... read »
May 23, 2013 at 3:49 PM
Strange Interaction Between DeserializeJson(), ArrayContains(), And Database Values In ColdFusion
@WebManWalking, I meant the code at the bottom (not the video). I did try to experiment with an intermediary variable, like: value = users.id[ i ]; arrayContains( userIDs, value ); ... but t ... read »
May 23, 2013 at 11:06 AM
Strange Interaction Between DeserializeJson(), ArrayContains(), And Database Values In ColdFusion
@Ben, Are you talking about As Number: YES As String: YES As Java: YES? If so, that's with 3 different ways of referencing the constant 1, not users.id[1]. Query object references(*) are what seem ... read »
InVision App - Prototyping Made Beautiful With Prototyping Tools