I would prefer to call the API lower level or one to calculate its CF in the same way as usual. I quit various services and do not see obvious calling methods that could help.
AFAIK, there is not a single linear solution. The main CF method is used to calculate the dates of CronTabEntry.NextRunTime
. The CronTabEntry
class represents options for a single task. The NextRunTime
method calculates the next potential NextRunTime
date based on the task settings. Outdated and paused tasks are handled elsewhere at run time.
To duplicate the results, you must call NextRunTime
and add a bit of logic to handle expired tasks. Although the NextRunTime
method is private, it can still be accessed through reflection using Method.setAccessible (boolean) .
I put together the function below to demonstrate. Its main part is the call to reflection (which is a bit more verbose in CF than its java equivalent). In any case, it should return the same dates used by the CF scheduler.
Rules:
- If the task completion date / time has passed, returns the string emtpy ""
- If this is a one-time task that has already been completed, returns an empty string ""
- For all other tasks (including suspended tasks) returns the next scheduled date
Application:
result = new TaskUtil().getNextRunTime("SomeTask"); WriteDump(result);
Cfc
component { public struct function getNextRunTime(required string scheduledTaskName) { // load task settings from factory local.cron = createobject("java","coldfusion.server.ServiceFactory").getCronService(); local.task = local.cron.findTask( arguments.scheduledTaskName ); // abort if we cannot find the task .. if ( isNull(local.task) ) { throw ("The specified task was not found: ["& arguments.scheduledTaskName &"]"); } // Calculate the next POTENTIAL schedule date local.isRecurring = listFindNoCase("daily,weekly,monthly", local.task.interval); local.dateClass = createObject("java", "java.util.Date").getClass(); local.longClass = createObject("java", "java.lang.Long").TYPE; local.stringClass = createObject("java", "java.lang.String").getClass(); // Construct the appropriate arguments // Note: must load arguments / class types into arrays for java reflection if (local.isRecurring) { local.args = [ local.task.getStartDate(), local.task.getStartTime(), local.task.interval ]; local.types = [ local.dateClass, local.dateClass, local.stringClass ]; } else { local.args = [ local.task.getStartDate(), local.task.getStartTime(), local.task.getEndTime(), javacast("long", val(local.task.interval) * 1000) ]; local.types = [ local.dateClass, local.dateClass, local.dateClass, local.longClass ]; } // Call CF internal method to calculate the next date (UNDOCUMENTED) local.internalMethod = local.task.getClass().getDeclaredMethod("NextRunTime", local.types ); local.internalMethod.setAccessible( true ); local.nextRunOnDate = local.internalMethod.invoke( local.task, local.args ); // Determine if the task will be rescheduled local.isExpired = false; if ( local.task.interval != "once" && structKeyExists( local.task, "end_date") ) { // It is non-recurring, so determine if it already expired local.expiresOnDate = local.task.mergeDates( local.task.getEndDate(), local.task.getEndTime() ); local.isExpired = dateCompare( local.nextRunOnDate, local.expiresOnDate, "s") >= 0; } else if ( local.task.interval == "once" && local.task.disabled) { // It is a one time task that already executed local.isExpired = true; } // construct and return results local.result = {}; local.result.name = local.task.task; local.result.isPaused = local.task.paused; local.result.isExpired = local.isExpired; local.result.nextRunTime = local.isExpired ? "" : local.nextRunOnDate; return local.result; } }