Package org.platformlayer.ops.schedule

Source Code of org.platformlayer.ops.schedule.SimpleJobScheduleCalculator

package org.platformlayer.ops.schedule;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.Random;

import com.fathomdb.TimeSpan;

public class SimpleJobScheduleCalculator implements JobScheduleCalculator {
  public static final Date SUGGESTED_BASE = new Date(2000 - 1900, 0, 1, 0, 0, 0);

  public final Date base;
  public final TimeSpan interval;

  static final double MIN_RANDOM_FRACTION = 0.2;
  static final Random random = new Random();

  static final TimeSpan TIME_SKEW_ALLOWANCE = new TimeSpan("15s");

  public SimpleJobScheduleCalculator(TimeSpan interval, Date base) {
    this.base = base;

    this.interval = interval;
    if (interval == null) {
      throw new IllegalArgumentException("Interval not specified.  Task=" + this);
    }
  }

  @Override
  public String toString() {
    String intervalString;

    if (interval.getTotalSeconds() == TimeSpan.ONE_DAY.getTotalSeconds()) {
      intervalString = " Daily";
      SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss Z", Locale.US);

      return dateFormat.format(base) + intervalString;
    } else if (interval.getTotalSeconds() == 7 * TimeSpan.ONE_DAY.getTotalSeconds()) {
      intervalString = " Weekly";
      SimpleDateFormat dateFormat = new SimpleDateFormat("EEE HH:mm:ss Z", Locale.US);

      return dateFormat.format(base) + intervalString;
    }

    intervalString = "Every " + interval.toPrettyString();

    if (base == null || SUGGESTED_BASE.equals(base)) {
      return intervalString;
    } else {
      return intervalString + " from " + base;
    }
  }

  public TimeSpan getInterval() {
    return interval;
  }

  public Date getBase() {
    return base;
  }

  protected static Date calculateNext(final Date base, final TimeSpan previousInterval,
      final TimeSpan currentInterval, final JobExecution previousExecution) {
    long now = System.currentTimeMillis();

    if (base == null) {
      // Unbased
      if (previousExecution == null) {
        // We avoid randomly choosing a really small fraction, as that tends to start up tasks before the system
        // is really ready
        double fraction;
        do {
          synchronized (random) {
            fraction = random.nextDouble();
          }
        } while (fraction < MIN_RANDOM_FRACTION);

        TimeSpan delay = TimeSpan.fromMilliseconds((long) (fraction * previousInterval.getTotalMilliseconds()));
        return new Date(now + delay.getTotalMilliseconds());
      } else {
        return currentInterval.addTo(previousExecution.getStartTimestamp());
      }
    } else {
      // Based e.g. every morning at 2AM
      long partial = (now - base.getTime()) % previousInterval.getTotalMilliseconds();
      long previous = now - partial;
      long next = previous + currentInterval.getTotalMilliseconds();

      if (previousExecution != null) {
        // Check the last execution against what we think the last execution should have been (with a bit of
        // skew tolerance)
        if (previousExecution.getStartTimestamp().getTime() <= (previous - TIME_SKEW_ALLOWANCE
            .getTotalMilliseconds())) {
          // We missed our previous execution, we should run asap

          // Introduce a random delay to stop the thundering herd
          long delay = (long) (random.nextDouble() * previousInterval.getTotalMilliseconds() / 10);
          long scheduleAt = now + delay;

          // Check that we're not going to schedule it just before the next run anyway
          // Just before = 1/10 th. So for a daily task running at 4AM, if it's later than
          // 2.4 hours before = 1:36AM. That seems fairly sensible.
          if (scheduleAt < (next - previousInterval.getTotalMilliseconds() / 10)) {
            return new Date(scheduleAt);
          }
        }
      }

      return new Date(next);
    }
  }

  @Override
  public Date calculateNext(JobExecution previousExecution) {
    return calculateNext(base, interval, interval, previousExecution);
  }
}
TOP

Related Classes of org.platformlayer.ops.schedule.SimpleJobScheduleCalculator

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.