All Downloads are FREE. Search and download functionalities are using the official Maven repository.

proj.zoie.hourglass.impl.HourGlassScheduler Maven / Gradle / Ivy

There is a newer version: 3.3.0
Show newest version
package proj.zoie.hourglass.impl;

import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Random;

import org.apache.log4j.Logger;

public class HourGlassScheduler
{
  public static final Logger log = Logger.getLogger(HourGlassScheduler.class.getName());
  private String _schedule;
  private final FREQUENCY _freq;
  private int[] _params = new int[6];
  private int _trimThreshold = Integer.MAX_VALUE;
  private boolean _appendOnly = true;
  private static ThreadLocal dateFormatter = new ThreadLocal()
  {
    @Override
    protected SimpleDateFormat initialValue()
    {
      return new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");
    }
  };
  public static enum FREQUENCY
  {
    MINUTELY, // good for use in testing
    HOURLY,
    DAILY,
  }
  public HourGlassScheduler(FREQUENCY freq, String schedule)
  {
    // format "ss mm hh [ss mm hh]"
    // We will pick a time schedule randomly between the first and second schedules.
    _schedule = schedule;
    _freq = freq;
    String [] param = _schedule.split(" ");
    for(int i = 0; i < Math.min(_params.length, param.length); i++)
    {
      _params[i] = parseParam(param[i]);
    }

    int size = _params.length / 2;
    if (param.length <= size)
    {
      // We have only one schedule.
      for (int i = 0; i < size; ++i)
      {
        _params[size + i] = _params[i];
      }
    }

    log.info("schedule: " + Arrays.toString(_params) + " frequenty: " + _freq);

    if (param.length > size)
    {
      // We have a start and an end.
      int start = _params[0] + (_params[1] * 60) + (_params[2] * 3600);
      int end = _params[3] + (_params[4] * 60) + (_params[5] * 3600);
      int gap = end - start;
      if (gap < 0) gap = gap + 86400 /* one day */;
      if (gap != 0)
      {
        start += new Random().nextInt(gap);
        start %= 86400 /* one day */;
        _params[0] = start % 60;
        _params[1] = (start % 3600) / 60;
        _params[2] = start / 3600;
      }
    }

    log.info("schedule final: " + Arrays.toString(_params) + " frequenty: " + _freq);
  }
  public HourGlassScheduler(FREQUENCY freq, String schedule, int trimThreshold)
  {
    this(freq, schedule);
    _trimThreshold = trimThreshold;
    log.info("schedule: " + Arrays.toString(_params) + " frequenty: " + _freq + " trimThreshold: keep last " + _trimThreshold + " rolling periods");
  }
  public HourGlassScheduler(FREQUENCY freq, String schedule, boolean appendOnly)
  {
    this(freq, schedule);
    _appendOnly = appendOnly;
  }
  public HourGlassScheduler(FREQUENCY freq, String schedule, boolean appendOnly, int trimThreshold)
  {
    this(freq, schedule, appendOnly);
    _trimThreshold = trimThreshold;
    log.info("schedule: " + Arrays.toString(_params) + " frequenty: " + _freq + " trimThreshold: keep last " + _trimThreshold + " rolling periods");
  }
  private int parseParam(String param)
  {
    if (param.indexOf('*')>=0) return 0;
    int ret = 0;
    try
    {
      ret = Integer.parseInt(param.trim());
    } catch(NumberFormatException e)
    {
      throw new IllegalArgumentException("Failed to instantiate HourGlassScheduler", e);
    }
    return ret;
  }
  public FREQUENCY getFreq()
  {
    return _freq;
  }
  public int getTrimThreshold()
  {
    return _trimThreshold;
  }
  public boolean isAppendOnly()
  {
    return _appendOnly;
  }
  protected Calendar getNextRoll()
  {
    long timenow = System.currentTimeMillis();
    Calendar next = Calendar.getInstance();
    next.setTimeInMillis(timenow);
    Calendar now = Calendar.getInstance();
    now.setTimeInMillis(timenow);
    switch(_freq)
    {
    case MINUTELY: // set the second so that every minute we switch over at the same second
      next.set(Calendar.SECOND, _params[0]);
      if (!next.after(now)) next.add(Calendar.MINUTE, 1);
      break;
    case HOURLY:   // set the second and minute so that we switch over at the same time in the hour
      next.set(Calendar.SECOND, _params[0]);
      next.set(Calendar.MINUTE, _params[1]);
      if (!next.after(now)) next.add(Calendar.HOUR_OF_DAY, 1);
      break;
    case DAILY:    // set hour, minute, second so that we switch over at the same time of the day
      next.set(Calendar.SECOND, _params[0]);
      next.set(Calendar.MINUTE, _params[1]);
      next.set(Calendar.HOUR_OF_DAY, _params[2]);
      if (!next.after(now)) next.add(Calendar.DAY_OF_MONTH, 1);
      break;
    }
    return next;
  }
  Calendar getCurrentRoll()
  {
    Calendar current = getNextRoll();
    switch(_freq)
    {
    case MINUTELY:
      current.add(Calendar.MINUTE, -1);
      break;
    case HOURLY:
      current.add(Calendar.HOUR_OF_DAY, -1);
      break;
    case DAILY:
      current.add(Calendar.DAY_OF_MONTH, -1);
      break;
    }
    return current;
  }
  public Calendar getTrimTime(Calendar now)
  {
    Calendar threshold = (Calendar) now.clone();
    int trimUnit = 60*60*24;
    switch(getFreq())
    {
    case MINUTELY:
      trimUnit = 60;
      break;
    case HOURLY:
      trimUnit = 60*60;
      break;
    case DAILY:
      trimUnit = 60*60*24;
      break;
    }
    threshold.add(Calendar.SECOND, - trimUnit * _trimThreshold);
    return threshold;
  }
  /**
   * convert a Calendar time to a folder name using GMT time string
   * @param cal
   * @return a String for folder name
   */
  public String getFolderName(Calendar cal)
  {
    return dateFormatter.get().format(cal.getTime());
  }
  @Override
  public String toString()
  {
    return "HourGlassScheduler:" + _freq + "  " + _schedule;
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy