In ColdFusion 9, is there a quick way to find out the next time that a scheduled task will attempt to run?
问题:
回答1:
I would rather call a lower level API or such to have CF calculate it in the same way it normally would. I've dumped the various services and see no obvious methods to call that would help.
AFAIK, there is no one line solution. The main method CF uses to calculate the dates is CronTabEntry.NextRunTime
. The CronTabEntry
class represents the settings for a single task. The NextRunTime
method(s) calculate the next potential run date, based on the task's settings. Handling of expired and paused tasks is done elsewhere, at runtime.
To duplicate the results you need to call NextRunTime
and add a bit of logic to handle expired tasks. While NextRunTime
method is private, it can still be accessed via reflection, with the help of Method.setAccessible(boolean).
I threw together the function below to demonstrate. The bulk of it is the reflection call (which is a bit more verbose in CF than its java equivalent). Anyway, it should return the same dates used by the CF scheduler.
Rules:
- If the task end date/time has passed, returns an emtpy string ""
- If it is a one-time task, that already executed, returns an empty string ""
- For all other tasks (including paused tasks), returns the next scheduled date
Usage:
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's 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;
}
}