How do I find out the next run time for a Schedule

2019-06-24 03:30发布

问题:

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;
    }    
}