models.Schedule.java Source code

Java tutorial

Introduction

Here is the source code for models.Schedule.java

Source

package models;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Table;
import javax.persistence.Transient;
import org.apache.commons.lang.builder.CompareToBuilder;
import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
import org.apache.commons.lang.builder.ToStringBuilder;

@Entity
@Table(name = "schedule")
/**
 * Cron like schedule defaulting to 'wide open, run all the time',
 * used in the context of a user time zone.
 *
 * Confidential Information.
 * Copyright (C) 2003, 2004, 2005, 2006 Eric Link, All rights reserved.
 * PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 * @see http://java.sun.com/j2se/1.5.0/docs/api/java/util/Calendar.html
 **/
public class Schedule extends MyModel {

    private static Logger logger = Logger.getLogger(Schedule.class.getName());
    /**
     * http://java.sun.com/j2se/1.5.0/docs/api/constant-values.html#java.util.Calendar.JANUARY
     * java.util.Calendar
     * public static final int    AM    0
     * public static final int    AM_PM    9
     * public static final int    APRIL    3
     * public static final int    AUGUST    7
     * public static final int    DATE    5
     * public static final int    DAY_OF_MONTH    5
     * public static final int    DAY_OF_WEEK    7
     * public static final int    DAY_OF_WEEK_IN_MONTH    8
     * public static final int    DAY_OF_YEAR    6
     * public static final int    DECEMBER    11
     * public static final int    DST_OFFSET    16
     * public static final int    ERA    0
     * public static final int    FEBRUARY    1
     * public static final int    FIELD_COUNT    17
     * public static final int    FRIDAY    6
     * public static final int    HOUR    10
     * public static final int    HOUR_OF_DAY    11
     * public static final int    JANUARY    0
     * public static final int    JULY    6
     * public static final int    JUNE    5
     * public static final int    MARCH    2
     * public static final int    MAY    4
     * public static final int    MILLISECOND    14
     * public static final int    MINUTE    12
     * public static final int    MONDAY    2
     * public static final int    MONTH    2
     * public static final int    NOVEMBER    10
     * public static final int    OCTOBER    9
     * public static final int    PM    1
     * public static final int    SATURDAY    7
     * public static final int    SECOND    13
     * public static final int    SEPTEMBER    8
     * public static final int    SUNDAY    1
     * public static final int    THURSDAY    5
     * public static final int    TUESDAY    3
     * public static final int    UNDECIMBER    12
     * public static final int    WEDNESDAY    4
     * public static final int    WEEK_OF_MONTH    4
     * public static final int    WEEK_OF_YEAR    3
     * public static final int    YEAR    1
     * public static final int    ZONE_OFFSET    15
     ***/
    @Transient
    /**
     * Used to allow default schedules when there is no persisted schedule
     **/
    final public static Schedule DEFAULT_WIDE_OPEN_SCHEDULE = new Schedule();
    //////    From the java.util.Calendar doco:
    //////    MINUTE          0-59
    @Transient
    final private static int MINUTE_START = 0;
    @Transient
    final private static int MINUTE_END = 59;
    @Column(name = "startMinute", nullable = false)
    private int startMinute = MINUTE_START;
    @Column(name = "endMinute", nullable = false)
    private int endMinute = MINUTE_END;
    //////    HOUR_OF_DAY     is used for the 24-hour clock (0-23)
    @Transient
    final private static int HOUR_OF_DAY_START = 0;
    @Transient
    final private static int HOUR_OF_DAY_END = 23;
    @Column(name = "startHourOfDay", nullable = false)
    private int startHourOfDay = HOUR_OF_DAY_START;
    @Column(name = "endHourOfDay", nullable = false)
    private int endHourOfDay = HOUR_OF_DAY_END;
    //////    DAY_OF_MONTH    The first day of the month has value 1.
    @Transient
    final private static int DAY_OF_MONTH_START = 1;
    @Transient
    final private static int DAY_OF_MONTH_END = 31;
    @Column(name = "startDayOfMonth", nullable = false)
    private int startDayOfMonth = DAY_OF_MONTH_START;
    @Column(name = "endDayOfMonth", nullable = false)
    private int endDayOfMonth = DAY_OF_MONTH_END;
    //////    MONTH           The first month of the year is JANUARY which is 0
    @Transient
    final private static int MONTH_START = Calendar.JANUARY;
    @Transient
    final private static int MONTH_END = Calendar.DECEMBER;
    @Column(name = "startMonth", nullable = false)
    private int startMonth = MONTH_START;
    @Column(name = "endMonth", nullable = false)
    private int endMonth = MONTH_END;
    //////    DAY_OF_WEEK     SUNDAY == 1, SATURDAY == 7
    @Transient
    final private static int DAY_OF_WEEK_START = Calendar.SUNDAY;
    @Transient
    final private static int DAY_OF_WEEK_END = Calendar.SATURDAY;
    @Column(name = "startDayOfWeek", nullable = false)
    private int startDayOfWeek = DAY_OF_WEEK_START;
    @Column(name = "endDayOfWeek", nullable = false)
    private int endDayOfWeek = DAY_OF_WEEK_END;
    //////    YEAR            This is a calendar-specific value; see subclass documentation.
    @Transient
    final private static int YEAR_START = 0;
    @Transient
    final private static int YEAR_END = 9999;
    @Column(name = "startYear", nullable = false)
    private int startYear = YEAR_START;
    @Column(name = "endYear", nullable = false)
    private int endYear = YEAR_END;
    @Column(name = "name", nullable = false)
    private String name;

    public Schedule() {
    }

    private boolean isDefaultRange(int start, int end, int UNIT_START, int UNIT_END) {
        // default values, wide open
        return start == UNIT_START && end == UNIT_END;
    }

    private boolean isRunnable(int start, int end, int UNIT_START, int UNIT_END, int value) {
        // default values, wide open
        if (isDefaultRange(start, end, UNIT_START, UNIT_END)) {
            return true;
        }

        // not crossing the unit boundary, do the simple test
        if (end >= start) {
            return value >= start && value <= end;
        }

        // range crosses the unit boundary
        if (start >= end) {
            return // test before the boundary
            (value >= start && value <= UNIT_END) || // test after the boundary
                    (value >= UNIT_START && value <= end);
        }

        // should never get here
        return false;
    }

    /**
     * @return next java timestamp when this may be run,
     * or 0 if it is not runnable in the next three months
     **/
    public long calculateNextRunnableTime(Calendar calendar) {

        if (isRunnable(calendar)) {
            return System.currentTimeMillis();
        } else {
            Calendar calendarCopy = Calendar.getInstance(calendar.getTimeZone());
            calendarCopy.setTimeInMillis(calendar.getTimeInMillis());
            Calendar calMaxInFutureToCalculate = Calendar.getInstance();
            calMaxInFutureToCalculate.add(Calendar.MONTH, 3);
            long maxInFutureToCalculateMillis = calMaxInFutureToCalculate.getTimeInMillis();

            while (!isRunnable(calendarCopy)) {
                calendarCopy.add(Calendar.MINUTE, 1);
                if (calendarCopy.getTimeInMillis() > maxInFutureToCalculateMillis) {
                    return 0; //1970
                }
            }

            return calendarCopy.getTimeInMillis();
        }

    }

    public boolean isRunnable(Calendar calendar) {
        return isRunnable(startMinute, endMinute, MINUTE_START, MINUTE_END, calendar.get(Calendar.MINUTE))
                && isRunnable(startHourOfDay, endHourOfDay, HOUR_OF_DAY_START, HOUR_OF_DAY_END,
                        calendar.get(Calendar.HOUR_OF_DAY))
                && isRunnable(startDayOfMonth, endDayOfMonth, DAY_OF_MONTH_START, DAY_OF_MONTH_END,
                        calendar.get(Calendar.DAY_OF_MONTH))
                && isRunnable(startDayOfWeek, endDayOfWeek, DAY_OF_WEEK_START, DAY_OF_WEEK_END,
                        calendar.get(Calendar.DAY_OF_WEEK))
                && isRunnable(startYear, endYear, YEAR_START, YEAR_END, calendar.get(Calendar.YEAR));
    }

    /**
     * if calendar is runnable, check last run. if last run is runnable,
     * there is a possible dupe.  if all non-default runnable units are ==,
     * then this is a dupe
     *
     * false if last run cal == cal
     *
     * @return true if the schedule should run (considering the last run time)
     **/
    public boolean isRunnable(Calendar lastRunCalendar, Calendar calendar) {
        if (!isActive()) {
            // don't run inactive schedules
            return false;
        }

        if (lastRunCalendar != null && !lastRunCalendar.getTimeZone().equals(calendar.getTimeZone())) {
            throw new IllegalArgumentException(
                    "Calendar time zones must be the same for comparison lastRunCalendar=" + lastRunCalendar
                            + ",calendar=" + calendar);
        }

        if (lastRunCalendar != null && lastRunCalendar.equals(calendar)) {
            //same as last run time, so don't run again
            logger.log(Level.FINE, "lastRunCalendar.equals( calendar ), returning false");
            return false;
        }

        boolean calendarIsRunnable = isRunnable(calendar);
        if (!calendarIsRunnable) {
            // no need to check lastRun time, already not elible to run
            logger.log(Level.FINE, "!calendarIsRunnable, returning false");
            return false;
        }

        if (lastRunCalendar == null) {
            logger.log(Level.FINE, "lastRunCalendar is null, returning calendarIsRunnable={0}", calendarIsRunnable);
            return calendarIsRunnable;
        }

        // calendar is runnable, so see if last calendar applies
        boolean lastRunCalendarIsRunnable = isRunnable(lastRunCalendar);
        if (!lastRunCalendarIsRunnable) {
            // lastRunCalendar does not apply, ignore it
            logger.log(Level.FINE, "!lastRunCalendarIsRunnable, returning calendarIsRunnable={0}",
                    calendarIsRunnable);
            return calendarIsRunnable;
        }

        // both calendars are runnable, so this could be a dupe run
        // if all non-default runnable units are ==, then this is a dupe run

        // 1 identify non-default
        List<Integer> nonDefaultUnits = identifyNonDefaultUnits();
        if (nonDefaultUnits.size() == 0) {
            // schedule is wide open, and calendars are not equal, so just use calendar
            logger.log(Level.FINE, "schedule is wide open, calendars not equal, returning calendarIsRunnable={0}",
                    calendarIsRunnable);
            return calendarIsRunnable;
        }

        // 2 if something is set, and all set units are equal, then it's a dupe
        //   if they are not all equal, it's runnable
        calendarIsRunnable = !areAllSetRangesUnitsEqual(lastRunCalendar, calendar, nonDefaultUnits);
        logger.log(Level.FINE, "checking for dupe units, returning calendarIsRunnable={0}", calendarIsRunnable);

        return calendarIsRunnable;
    }

    private boolean areAllSetRangesUnitsEqual(final Calendar lastRunCalendar, final Calendar calendar,
            final List<Integer> nonDefaultUnits) {
        boolean eq = true;

        for (int unitType : nonDefaultUnits) {
            switch (unitType) {
            case Calendar.MINUTE:
                eq = (lastRunCalendar.get(Calendar.MINUTE) == calendar.get(Calendar.MINUTE)
                        && lastRunCalendar.get(Calendar.HOUR_OF_DAY) == calendar.get(Calendar.HOUR_OF_DAY)
                        && lastRunCalendar.get(Calendar.DAY_OF_YEAR) == calendar.get(Calendar.DAY_OF_YEAR)
                        && lastRunCalendar.get(Calendar.YEAR) == calendar.get(Calendar.YEAR));
                break;
            case Calendar.HOUR_OF_DAY:
                eq = (lastRunCalendar.get(Calendar.HOUR_OF_DAY) == calendar.get(Calendar.HOUR_OF_DAY)
                        && lastRunCalendar.get(Calendar.DAY_OF_YEAR) == calendar.get(Calendar.DAY_OF_YEAR)
                        && lastRunCalendar.get(Calendar.YEAR) == calendar.get(Calendar.YEAR));
                break;
            case Calendar.DAY_OF_MONTH:
                eq = (lastRunCalendar.get(Calendar.DAY_OF_MONTH) == calendar.get(Calendar.DAY_OF_MONTH)
                        && lastRunCalendar.get(Calendar.DAY_OF_YEAR) == calendar.get(Calendar.DAY_OF_YEAR)
                        && lastRunCalendar.get(Calendar.YEAR) == calendar.get(Calendar.YEAR));
                break;
            case Calendar.DAY_OF_WEEK:
                eq = (lastRunCalendar.get(Calendar.DAY_OF_WEEK) == calendar.get(Calendar.DAY_OF_WEEK)
                        && lastRunCalendar.get(Calendar.DAY_OF_YEAR) == calendar.get(Calendar.DAY_OF_YEAR)
                        && lastRunCalendar.get(Calendar.YEAR) == calendar.get(Calendar.YEAR));
                break;
            case Calendar.YEAR:
                eq = lastRunCalendar.get(Calendar.YEAR) == calendar.get(Calendar.YEAR);
                break;
            }

            if (!eq) {
                // any one unit check is different, so not all equal
                return eq;
            }
        }

        return eq; // they were _all_ equal
    }

    private List<Integer> identifyNonDefaultUnits() {
        List<Integer> nonDefaultUnits = new ArrayList<Integer>();

        if (!isDefaultRange(startMinute, endMinute, MINUTE_START, MINUTE_END)) {
            nonDefaultUnits.add(Calendar.MINUTE);
        }
        if (!isDefaultRange(startHourOfDay, endHourOfDay, HOUR_OF_DAY_START, HOUR_OF_DAY_END)) {
            nonDefaultUnits.add(Calendar.HOUR_OF_DAY);
        }
        if (!isDefaultRange(startDayOfMonth, endDayOfMonth, DAY_OF_MONTH_START, DAY_OF_MONTH_END)) {
            nonDefaultUnits.add(Calendar.DAY_OF_MONTH);
        }
        if (!isDefaultRange(startDayOfWeek, endDayOfWeek, DAY_OF_WEEK_START, DAY_OF_WEEK_END)) {
            nonDefaultUnits.add(Calendar.DAY_OF_WEEK);
        }
        if (!isDefaultRange(startYear, endYear, YEAR_START, YEAR_END)) {
            nonDefaultUnits.add(Calendar.YEAR);
        }
        return nonDefaultUnits;
    }

    public boolean equals(Object obj) {
        if (obj instanceof Schedule == false) {
            return false;
        }
        if (this == obj) {
            return true;
        }
        Schedule rhs = (Schedule) obj;
        return new EqualsBuilder().append(startDayOfMonth, rhs.startDayOfMonth)
                .append(startDayOfWeek, rhs.startDayOfWeek).append(startHourOfDay, rhs.startHourOfDay)
                .append(startMinute, rhs.startMinute).append(startMonth, rhs.startMonth)
                .append(startYear, rhs.startYear).append(endDayOfMonth, rhs.endDayOfMonth)
                .append(endDayOfWeek, rhs.endDayOfWeek).append(endHourOfDay, rhs.endHourOfDay)
                .append(endMinute, rhs.endMinute).append(endMonth, rhs.endMonth).append(endYear, rhs.endYear)
                .append(name, rhs.name).isEquals();
    }

    public int compareTo(Object o) {
        Schedule rhs = (Schedule) o;
        return new CompareToBuilder().append(startDayOfMonth, rhs.startDayOfMonth)
                .append(startDayOfWeek, rhs.startDayOfWeek).append(startHourOfDay, rhs.startHourOfDay)
                .append(startMinute, rhs.startMinute).append(startMonth, rhs.startMonth)
                .append(startYear, rhs.startYear).append(endDayOfMonth, rhs.endDayOfMonth)
                .append(endDayOfWeek, rhs.endDayOfWeek).append(endHourOfDay, rhs.endHourOfDay)
                .append(endMinute, rhs.endMinute).append(endMonth, rhs.endMonth).append(endYear, rhs.endYear)
                .append(name, rhs.name).toComparison();
    }

    public int hashCode() {
        return new HashCodeBuilder().append(startDayOfMonth).append(startDayOfWeek).append(startHourOfDay)
                .append(startMinute).append(startMonth).append(startYear).append(endDayOfMonth).append(endDayOfWeek)
                .append(endHourOfDay).append(endMinute).append(endMonth).append(endYear).append(name).toHashCode();
    }

    public String toString() {
        return new ToStringBuilder(this).append("id", id).append("startYear", startYear).append("endYear", endYear)
                .append("startMonth", startMonth).append("endMonth", endMonth)
                .append("startDayOfMonth", startDayOfMonth).append("endDayOfMonth", endDayOfMonth)
                .append("startDayOfWeek", startDayOfWeek).append("endDayOfWeek", endDayOfWeek)
                .append("startHourOfDay", startHourOfDay).append("endHourOfDay", endHourOfDay)
                .append("startMinute", startMinute).append("endMinute", endMinute).append("name", name).toString();
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getStartMinute() {
        return startMinute;
    }

    public void setMinuteRange(int startMinute, int endMinute) {
        this.startMinute = startMinute;
        this.endMinute = endMinute;
    }

    public int getEndMinute() {
        return endMinute;
    }

    public int getStartHourOfDay() {
        return startHourOfDay;
    }

    public int getEndHourOfDay() {
        return endHourOfDay;
    }

    public void setHourOfDayRange(int startHourOfDay, int endHourOfDay) {
        this.startHourOfDay = startHourOfDay;
        this.endHourOfDay = endHourOfDay;
    }

    public int getStartDayOfMonth() {
        return startDayOfMonth;
    }

    public int getEndDayOfMonth() {
        return endDayOfMonth;
    }

    public void setDayOfMonthRange(int startDayOfMonth, int endDayOfMonth) {
        this.startDayOfMonth = startDayOfMonth;
        this.endDayOfMonth = endDayOfMonth;
    }

    public int getStartMonth() {
        return startMonth;
    }

    public int getEndMonth() {
        return endMonth;
    }

    public void setMonthRange(int startMonth, int endMonth) {
        this.startMonth = startMonth;
        this.endMonth = endMonth;
    }

    public int getStartDayOfWeek() {
        return startDayOfWeek;
    }

    public int getEndDayOfWeek() {
        return endDayOfWeek;
    }

    public void setDayOfWeekRange(int startDayOfWeek, int endDayOfWeek) {
        this.startDayOfWeek = startDayOfWeek;
        this.endDayOfWeek = endDayOfWeek;
    }

    public int getStartYear() {
        return startYear;
    }

    public int getEndYear() {
        return endYear;
    }

    public void setYearRange(int startYear, int endYear) {
        this.startYear = startYear;
        this.endYear = endYear;
    }

    public boolean isActive() {
        return isActive;
    }

    public void setIsActive(boolean isActive) {
        this.isActive = isActive;
    }
}