Quartz cold start

Quartz Recently I've worked on one of the projects that is being used Quartz library. We've faced with the problem when the application is deployed for the first time - it is not possible to get the previous fired time from Quartz framework. So there is nothing left like implementing this on our own.

Overview

We need the previous time because the clients need to get the data that were fetched from the other service using the timeframe based on cron expressions.
Digging through documentation gave the result - found the method that I was looking for:
getTimeBefore();

When I've looked into a method - found that it was not implemented yet. By default it returned null (It's oddd why they haven't decided to throw an exception for e.g. UnsupportedOperationException). Due to the lack of this method - I've decided to implement in on my own.

Implementating

This method is of course not the best solution, becauses it is using the brute force technique. But anyway it should be used only once, for the first-time run (cold start) and then you could use standard method to retrieve previous time. The idea of method is to work with cron expression directly by sorting out values.
Below is the method that will NOT work for the first time-run:

Date lastFiredJobTime = jobExecutionContext.getPreviousFireTime();  

The way to achive this is to get the scheduled fire time first:

Date scheduledCurrentJobTime = jobExecutionContext.getScheduledFireTime();  

Cut the long story short, below is the workaround solution for the cold start problem:

public class CustomCronExpression extends CronExpression {  
    private static final String DELIMITER = " ";
    /**
     * Constructs a new <CODE>CronExpression</CODE> based 
     * on the specified parameter.
     * @param cronExpression String 
     * representation of the cron expression the
     * new object should represent
     * @throws java.text.ParseException if the string expression 
     * cannot be parsed into a valid
     * <CODE>CronExpression</CODE>
     */

  public CustomCronExpression(String cronExpression) throws ParseException {
      super(cronExpression);
  }

  // here we can override this method and call it in future for the first-time 
  // lauch to get previous time
    @Override
    public Date getTimeBefore(Date targetDate) {
        Date nextFireTime = getTimeAfter(targetDate);

        Calendar calendar = Calendar.getInstance(getTimeZone());
        calendar.setTime(nextFireTime);
        calendar.add(Calendar.SECOND, -1);

        int dateUnit = getDateUnitCronExpression();

      switch (dateUnit) {
          case -1:
             break;
          case MINUTE:
              calendar.add(Calendar.MINUTE, -1);
              break;
          case HOUR:
              calendar.add(Calendar.HOUR_OF_DAY, -1); 
              break;
          case DAY_OF_MONTH:
          case DAY_OF_WEEK:
              calendar.add(Calendar.DAY_OF_YEAR, -1);
              break;
          case MONTH:
              calendar.add(Calendar.MONTH, -1);
              break;
          default:
              calendar.add(Calendar.YEAR, -1);
              break;
    }

    Date previousDate = getTimeAfter(calendar.getTime());
    Date afterPreviousDate = getTimeAfter(previousDate);
    return findPreviousJobFireDate(afterPreviousDate, targetDate);
  }

  private int getDateUnitCronExpression() {
      String cronExpression = getCronExpression();
      if (StringUtils.isEmpty(cronExpression)) {
          return -1;
      }

      String[] expression = cronExpression.split(DELIMITER);
      return findDateUnitToWorkWith(expression);
  }

  private Date findPreviousJobFireDate(Date afterFuturePreviousDate, Date targetDate) {
    Date futureDate = afterFuturePreviousDate;
    Date resultDate = targetDate;

    while (true) {
        if (futureDate.equals(targetDate)) {
           return resultDate;
        } else {
           resultDate = futureDate;
           futureDate = getTimeAfter(resultDate);
           // prevent NPE and return current job time
           if(futureDate == null) {
             return resultDate;
           }
        }
     }
  }

  private int findDateUnitToWorkWith(String[] expression) {
    // * - represent the unit of date in cron expression
    // [0]Sec [1]Min [2]Hour [3]DayOfMonth [4]Month [5]DayOfWeek [6]Year
    for (int i = 0, n = expression.length; i < n; i++) {
        if (expression[i].equals("*")) {
            return i;
        }
    }
    return YEAR;
  }

}

For example, we have next CronExpression like: 0 15 10 * * ?

findDateUnitToWorkWith(String[] expression) - this method will find first time * in cron expression. The method will search in this case for the first occurence of * and substract the next unit of time in cron expression. Then it will manually go through the next fire time. When we reach it then we should take the previous time and it will be actually our required time that we were looking for.