/**
  * Loop over all currently started tasks and cycle them. This should be done after the classloader
  * has been changed (e.g. during module start/stop)
  */
 public void rescheduleAllTasks() throws SchedulerException {
   for (TaskDefinition task : getScheduledTasks()) {
     try {
       rescheduleTask(task);
     } catch (SchedulerException e) {
       log.error("Failed to restart task: " + task.getName(), e);
     }
   }
 }
  /**
   * Saves and stops all active tasks
   *
   * @return OpenmrsMemento
   */
  public OpenmrsMemento saveToMemento() {

    Set<Integer> tasks = new HashSet<Integer>();

    for (TaskDefinition task : getScheduledTasks()) {
      tasks.add(task.getId());
      try {
        shutdownTask(task);
      } catch (SchedulerException e) {
        // just swallow exceptions
        log.debug("Failed to stop task while saving memento " + task.getName(), e);
      }
    }

    TimerSchedulerMemento memento = new TimerSchedulerMemento(tasks);
    memento.saveErrorTasks();

    return memento;
  }
  /**
   * Schedule the given task according to the given schedule.
   *
   * @param taskDefinition the task to be scheduled
   * @should should handle zero repeat interval
   */
  public Task scheduleTask(TaskDefinition taskDefinition) throws SchedulerException {
    Task clientTask = null;
    if (taskDefinition != null) {

      // Cancel any existing timer tasks for the same task definition
      // TODO Make sure this is the desired behavior
      // TODO Do we ever want the same task definition to run more than once?
      TimerSchedulerTask schedulerTask = scheduledTasks.get(taskDefinition.getId());
      if (schedulerTask != null) {
        // schedulerTask.cancel();
        log.info("Shutting down the existing instance of this task to avoid conflicts!!");
        schedulerTask.shutdown();
      }

      try {

        // Create new task from task definition
        clientTask = TaskFactory.getInstance().createInstance(taskDefinition);

        // if we were unable to get a class, just quit
        if (clientTask != null) {

          schedulerTask = new TimerSchedulerTask(clientTask);
          taskDefinition.setTaskInstance(clientTask);

          // Once this method is called, the timer is set to start at the given start time.
          // NOTE:  We need to adjust the repeat interval as the JDK Timer expects time in
          // milliseconds and
          // we record by seconds.

          long repeatInterval = 0;
          if (taskDefinition.getRepeatInterval() != null)
            repeatInterval =
                taskDefinition.getRepeatInterval() * SchedulerConstants.SCHEDULER_MILLIS_PER_SECOND;

          if (taskDefinition.getStartTime() != null) {
            // Need to calculate the "next execution time" because the scheduled time is most likely
            // in the past
            // and the JDK timer will run the task X number of times from the start time until now
            // to catch up.
            Date nextTime = SchedulerUtil.getNextExecution(taskDefinition);

            // Start task at fixed rate at given future date and repeat as directed
            log.info("Starting task ... the task will execute for the first time at " + nextTime);

            if (repeatInterval > 0) {
              // Schedule the task to run at a fixed rate
              getTimer(taskDefinition).scheduleAtFixedRate(schedulerTask, nextTime, repeatInterval);
            } else {
              // Schedule the task to be non-repeating
              getTimer(taskDefinition).schedule(schedulerTask, nextTime);
            }

          } else if (repeatInterval > 0) {
            // Start task on repeating schedule, delay for SCHEDULER_DEFAULT_DELAY seconds
            log.info(
                "Delaying start time by "
                    + SchedulerConstants.SCHEDULER_DEFAULT_DELAY
                    + " seconds");
            getTimer(taskDefinition)
                .scheduleAtFixedRate(
                    schedulerTask, SchedulerConstants.SCHEDULER_DEFAULT_DELAY, repeatInterval);
          } else {
            // schedule for single execution, starting now
            log.info("Starting one-shot task");
            getTimer(taskDefinition).schedule(schedulerTask, new Date());
          }

          // Update task that has been started
          log.debug("Registering timer for task " + taskDefinition.getId());

          //  Add the new timer to the scheduler running task list
          scheduledTasks.put(taskDefinition.getId(), schedulerTask);

          // Update the timer status in the database
          taskDefinition.setStarted(true);
          saveTask(taskDefinition);
        }
      } catch (Exception e) {
        log.error("Failed to schedule task " + taskDefinition.getName(), e);
        throw new SchedulerException("Failed to schedule task", e);
      }
    }
    return clientTask;
  }