/**
   * Restores persisted timers, corresponding to this timerservice, which are eligible for any new
   * timeouts.
   *
   * <p>This includes timers whose {@link TimerState} is <b>neither</b> of the following:
   *
   * <ul>
   *   <li>{@link TimerState#CANCELED}
   *   <li>{@link TimerState#EXPIRED}
   * </ul>
   *
   * <p>All such restored timers will be schedule for their next timeouts.
   *
   * @param autoTimers
   */
  public void restoreTimers(final List<ScheduleTimer> autoTimers) {
    // get the persisted timers which are considered active
    List<TimerImpl> restorableTimers = this.getActivePersistentTimers();

    // timers are removed from the list as they are loaded
    final List<ScheduleTimer> newAutoTimers = new LinkedList<ScheduleTimer>(autoTimers);

    ROOT_LOGGER.debug(
        "Found "
            + restorableTimers.size()
            + " active persistentTimers for timedObjectId: "
            + getInvoker().getTimedObjectId());
    // now "start" each of the restorable timer. This involves, moving the timer to an ACTIVE state
    // and scheduling the timer task
    for (final TimerImpl activeTimer : restorableTimers) {

      if (activeTimer.isAutoTimer()) {
        CalendarTimer calendarTimer = (CalendarTimer) activeTimer;
        boolean found = false;
        // so we know we have an auto timer. We need to try and match it up with the auto timers.
        ListIterator<ScheduleTimer> it = newAutoTimers.listIterator();
        while (it.hasNext()) {
          ScheduleTimer timer = it.next();
          final String methodName = timer.getMethod().getName();
          final String[] params = new String[timer.getMethod().getParameterTypes().length];
          for (int i = 0; i < timer.getMethod().getParameterTypes().length; ++i) {
            params[i] = timer.getMethod().getParameterTypes()[i].getName();
          }
          if (doesTimeoutMethodMatch(calendarTimer.getTimeoutMethod(), methodName, params)) {

            // the timers have the same method.
            // now lets make sure the schedule is the same
            // and the timer does not change the persistence
            if (this.doesScheduleMatch(
                    calendarTimer.getScheduleExpression(), timer.getScheduleExpression())
                && timer.getTimerConfig().isPersistent()) {
              it.remove();
              found = true;
              break;
            }
          }
        }
        if (!found) {
          activeTimer.setTimerState(TimerState.CANCELED);
        } else {
          startTimer(activeTimer);
          ROOT_LOGGER.debug("Started timer: " + activeTimer);
        }
        this.persistTimer(activeTimer, false);
      } else if (!ineligibleTimerStates.contains(activeTimer.getState())) {
        startTimer(activeTimer);
      }
      ROOT_LOGGER.debug("Started timer: " + activeTimer);
    }

    for (ScheduleTimer timer : newAutoTimers) {
      this.loadAutoTimer(timer.getScheduleExpression(), timer.getTimerConfig(), timer.getMethod());
    }
  }
  /**
   * Creates a calendar based {@link javax.ejb.Timer}
   *
   * @param schedule The {@link javax.ejb.ScheduleExpression} which will be used for creating
   *     scheduled timer tasks for a calendar based timer
   * @param info {@link java.io.Serializable} info that will be made available through the newly
   *     created timer's {@link javax.ejb.Timer#getInfo()} method
   * @param persistent True if the newly created timer has to be persistent
   * @return Returns the newly created timer
   * @throws IllegalArgumentException If the passed <code>schedule</code> is null
   * @throws IllegalStateException If this method was invoked during a lifecycle callback on the EJB
   */
  private TimerImpl createCalendarTimer(
      ScheduleExpression schedule, Serializable info, boolean persistent, Method timeoutMethod) {
    if (this.isLifecycleCallbackInvocation() && !this.isSingletonBeanInvocation()) {
      throw EjbLogger.ROOT_LOGGER.failToCreateTimerDoLifecycle();
    }
    if (schedule == null) {
      throw EjbLogger.ROOT_LOGGER.scheduleIsNull();
    }
    // generate an id for the timer
    UUID uuid = UUID.randomUUID();
    // create the timer
    TimerImpl timer =
        CalendarTimer.builder()
            .setAutoTimer(timeoutMethod != null)
            .setScheduleExprSecond(schedule.getSecond())
            .setScheduleExprMinute(schedule.getMinute())
            .setScheduleExprHour(schedule.getHour())
            .setScheduleExprDayOfWeek(schedule.getDayOfWeek())
            .setScheduleExprDayOfMonth(schedule.getDayOfMonth())
            .setScheduleExprMonth(schedule.getMonth())
            .setScheduleExprYear(schedule.getYear())
            .setScheduleExprStartDate(schedule.getStart())
            .setScheduleExprEndDate(schedule.getEnd())
            .setScheduleExprTimezone(schedule.getTimezone())
            .setTimeoutMethod(timeoutMethod)
            .setTimerState(TimerState.CREATED)
            .setId(uuid.toString())
            .setPersistent(persistent)
            .setPrimaryKey(currentPrimaryKey())
            .setTimedObjectId(getInvoker().getTimedObjectId())
            .setInfo(info)
            .setNewTimer(true)
            .build(this);

    this.persistTimer(timer, true);
    // now "start" the timer. This involves, moving the timer to an ACTIVE state
    // and scheduling the timer task
    this.startTimer(timer);
    // return the timer
    return timer;
  }