org.jboss.as.ejb3.timerservice.schedule.CalendarBasedTimeout Maven / Gradle / Ivy
The newest version!
/*
* JBoss, Home of Professional Open Source.
* Copyright 2009, Red Hat Middleware LLC, and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.as.ejb3.timerservice.schedule;
import org.jboss.as.ejb3.timerservice.schedule.attribute.DayOfMonth;
import org.jboss.as.ejb3.timerservice.schedule.attribute.DayOfWeek;
import org.jboss.as.ejb3.timerservice.schedule.attribute.Hour;
import org.jboss.as.ejb3.timerservice.schedule.attribute.Minute;
import org.jboss.as.ejb3.timerservice.schedule.attribute.Month;
import org.jboss.as.ejb3.timerservice.schedule.attribute.Second;
import org.jboss.as.ejb3.timerservice.schedule.attribute.Year;
import javax.ejb.ScheduleExpression;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.TimeZone;
import static org.jboss.as.ejb3.EjbMessages.MESSAGES;
import static org.jboss.as.ejb3.EjbLogger.ROOT_LOGGER;
/**
* CalendarBasedTimeout
*
* @author Jaikiran Pai
* @version $Revision: $
*/
public class CalendarBasedTimeout {
/**
* The {@link javax.ejb.ScheduleExpression} from which this {@link CalendarBasedTimeout}
* was created
*/
private ScheduleExpression scheduleExpression;
/**
* The {@link Second} created out of the {@link javax.ejb.ScheduleExpression#getSecond()} value
*/
private Second second;
/**
* The {@link org.jboss.as.ejb3.timerservice.schedule.attribute.Minute} created out of the {@link javax.ejb.ScheduleExpression#getMinute()} value
*/
private Minute minute;
/**
* The {@link org.jboss.as.ejb3.timerservice.schedule.attribute.Hour} created out of the {@link javax.ejb.ScheduleExpression#getHour()} value
*/
private Hour hour;
/**
* The {@link DayOfWeek} created out of the {@link javax.ejb.ScheduleExpression#getDayOfWeek()} value
*/
private DayOfWeek dayOfWeek;
/**
* The {@link org.jboss.as.ejb3.timerservice.schedule.attribute.DayOfMonth} created out of the {@link javax.ejb.ScheduleExpression#getDayOfMonth()} value
*/
private DayOfMonth dayOfMonth;
/**
* The {@link Month} created out of the {@link javax.ejb.ScheduleExpression#getMonth()} value
*/
private Month month;
/**
* The {@link org.jboss.as.ejb3.timerservice.schedule.attribute.Year} created out of the {@link javax.ejb.ScheduleExpression#getYear()} value
*/
private Year year;
/**
* The first timeout relative to the time when this {@link CalendarBasedTimeout} was created
* from a {@link javax.ejb.ScheduleExpression}
*/
private Calendar firstTimeout;
/**
* The timezone being used for this {@link CalendarBasedTimeout}
*/
private TimeZone timezone;
/**
* Creates a {@link CalendarBasedTimeout} from the passed schedule
.
*
* This constructor parses the passed {@link javax.ejb.ScheduleExpression} and sets up
* its internal representation of the same.
*
*
* @param schedule The schedule
*/
public CalendarBasedTimeout(ScheduleExpression schedule) {
if (schedule == null) {
throw MESSAGES.invalidScheduleExpression(this.getClass().getName());
}
// make sure that the schedule doesn't have null values for its various attributes
this.nullCheckScheduleAttributes(schedule);
// store the original expression from which this
// CalendarBasedTimeout was created. Since the ScheduleExpression
// is mutable, we will have to store a clone copy of the schedule,
// so that any subsequent changes after the CalendarBasedTimeout construction,
// do not affect this internal schedule expression.
this.scheduleExpression = this.clone(schedule);
// Start parsing the values in the ScheduleExpression
this.second = new Second(schedule.getSecond());
this.minute = new Minute(schedule.getMinute());
this.hour = new Hour(schedule.getHour());
this.dayOfWeek = new DayOfWeek(schedule.getDayOfWeek());
this.dayOfMonth = new DayOfMonth(schedule.getDayOfMonth());
this.month = new Month(schedule.getMonth());
this.year = new Year(schedule.getYear());
if (schedule.getTimezone() != null && schedule.getTimezone().trim().isEmpty() == false) {
// If the timezone ID wasn't valid, then Timezone.getTimeZone returns
// GMT, which may not always be desirable.
// So we first check to see if the timezone id specified is available in
// timezone ids in the system. If it's available then we log a WARN message
// and fallback on the server's timezone.
String timezoneId = schedule.getTimezone();
String[] availableTimeZoneIDs = TimeZone.getAvailableIDs();
if (availableTimeZoneIDs != null && Arrays.asList(availableTimeZoneIDs).contains(timezoneId)) {
this.timezone = TimeZone.getTimeZone(timezoneId);
} else {
ROOT_LOGGER.unknownTimezoneId(timezoneId, TimeZone.getDefault().getID());
// use server's timezone
this.timezone = TimeZone.getDefault();
}
} else {
this.timezone = TimeZone.getDefault();
}
// Now that we have parsed the values from the ScheduleExpression,
// determine and set the first timeout (relative to the current time)
// of this CalendarBasedTimeout
this.setFirstTimeout();
}
public Calendar getNextTimeout() {
Calendar now = new GregorianCalendar(this.timezone);
now.setTime(new Date());
return this.getNextTimeout(now);
}
/**
* @return
*/
public Calendar getFirstTimeout() {
return this.firstTimeout;
}
private void setFirstTimeout() {
this.firstTimeout = new GregorianCalendar(this.timezone);
Date start = this.scheduleExpression.getStart();
if (start != null) {
this.firstTimeout.setTime(start);
} else {
this.firstTimeout.set(Calendar.SECOND, this.second.getFirst());
this.firstTimeout.set(Calendar.MINUTE, this.minute.getFirst());
this.firstTimeout.set(Calendar.HOUR_OF_DAY, this.hour.getFirst());
this.firstTimeout.set(Calendar.MILLISECOND, 0);
}
this.firstTimeout.setFirstDayOfWeek(Calendar.SUNDAY);
this.firstTimeout = this.computeNextSecond(this.firstTimeout);
if (this.firstTimeout == null) {
return;
}
this.firstTimeout = this.computeNextMinute(this.firstTimeout);
if (this.firstTimeout == null) {
return;
}
this.firstTimeout = this.computeNextHour(this.firstTimeout);
if (this.firstTimeout == null) {
return;
}
this.firstTimeout = this.computeNextMonth(this.firstTimeout);
if (this.firstTimeout == null) {
return;
}
this.firstTimeout = this.computeNextDate(this.firstTimeout);
if (this.firstTimeout == null) {
return;
}
this.firstTimeout = this.computeNextYear(this.firstTimeout);
// one final check
if (this.firstTimeout != null && this.noMoreTimeouts(this.firstTimeout)) {
this.firstTimeout = null;
}
}
/**
* Returns the original {@link javax.ejb.ScheduleExpression} from which this {@link CalendarBasedTimeout}
* was created.
*
* @return
*/
public ScheduleExpression getScheduleExpression() {
return this.scheduleExpression;
}
public Calendar getNextTimeout(Calendar currentCal) {
if (this.noMoreTimeouts(currentCal)) {
return null;
}
Calendar nextCal = this.copy(currentCal);
Date start = this.scheduleExpression.getStart();
if (start != null && currentCal.getTime().before(start)) {
nextCal.setTime(start);
} else {
// increment the current second by 1
nextCal.add(Calendar.SECOND, 1);
nextCal.set(Calendar.MILLISECOND, 0);
}
nextCal.setFirstDayOfWeek(Calendar.SUNDAY);
nextCal = this.computeNextSecond(nextCal);
if (nextCal == null) {
return null;
}
nextCal = this.computeNextMinute(nextCal);
if (nextCal == null) {
return null;
}
nextCal = this.computeNextHour(nextCal);
if (nextCal == null) {
return null;
}
nextCal = this.computeNextMonth(nextCal);
if (nextCal == null) {
return null;
}
nextCal = this.computeNextDate(nextCal);
if (nextCal == null) {
return null;
}
nextCal = this.computeNextYear(nextCal);
if (nextCal == null) {
return null;
}
// one final check
if (this.noMoreTimeouts(nextCal)) {
return null;
}
return nextCal;
}
private Calendar computeNextSecond(Calendar currentCal) {
if (this.noMoreTimeouts(currentCal)) {
return null;
}
Integer nextSecond = this.second.getNextMatch(currentCal);
if (nextSecond == null) {
return null;
}
int currentSecond = currentCal.get(Calendar.SECOND);
// if the current second is a match, then nothing else to
// do. Just return back the calendar
if (currentSecond == nextSecond) {
return currentCal;
}
Calendar nextCal = this.copy(currentCal);
// At this point, a suitable "next" second has been identified.
// There can be 2 cases
// 1) The "next" second is greater than the current second : This
// implies that the next second is within the "current" minute.
// 2) The "next" second is lesser than the current second : This implies
// that the next second is in the next minute (i.e. current minute needs to
// be advanced to next minute).
// handle case#1
if (nextSecond > currentSecond) {
nextCal.set(Calendar.SECOND, nextSecond);
return nextCal;
}
// case#2
if (nextSecond < currentSecond) {
nextCal.set(Calendar.SECOND, nextSecond);
// advance the minute to next minute
nextCal.add(Calendar.MINUTE, 1);
return nextCal;
}
return null;
}
private Calendar computeNextMinute(Calendar currentCal) {
if (this.noMoreTimeouts(currentCal)) {
return null;
}
Integer nextMinute = this.minute.getNextMatch(currentCal);
if (nextMinute == null) {
return null;
}
int currentMinute = currentCal.get(Calendar.MINUTE);
// if the current minute is a match, then nothing else to
// do. Just return back the calendar
if (currentMinute == nextMinute) {
return currentCal;
}
Calendar nextCal = this.copy(currentCal);
// At this point, a suitable "next" minute has been identified.
// There can be 2 cases
// 1) The "next" minute is greater than the current minute : This
// implies that the next minute is within the "current" hour.
// 2) The "next" minute is lesser than the current minute : This implies
// that the next minute is in the next hour (i.e. current hour needs to
// be advanced to next hour).
// handle case#1
if (nextMinute > currentMinute) {
// set the chosen minute
nextCal.set(Calendar.MINUTE, nextMinute);
// since we are moving to a different minute (as compared to the current minute),
// we should reset the second, to its first possible value
nextCal.set(Calendar.SECOND, this.second.getFirst());
return nextCal;
}
// case#2
if (nextMinute < currentMinute) {
// since we are advancing the hour, we should
// restart from the first eligible second
nextCal.set(Calendar.SECOND, this.second.getFirst());
// set the chosen minute
nextCal.set(Calendar.MINUTE, nextMinute);
// advance the hour to next hour
nextCal.add(Calendar.HOUR_OF_DAY, 1);
return nextCal;
}
return null;
}
private Calendar computeNextHour(Calendar currentCal) {
if (this.noMoreTimeouts(currentCal)) {
return null;
}
Integer nextHour = this.hour.getNextMatch(currentCal);
if (nextHour == null) {
return null;
}
int currentHour = currentCal.get(Calendar.HOUR_OF_DAY);
// if the current hour is a match, then nothing else to
// do. Just return back the calendar
if (currentHour == nextHour) {
return currentCal;
}
Calendar nextCal = this.copy(currentCal);
// At this point, a suitable "next" hour has been identified.
// There can be 2 cases
// 1) The "next" hour is greater than the current hour : This
// implies that the next hour is within the "current" day.
// 2) The "next" hour is lesser than the current hour : This implies
// that the next hour is in the next day (i.e. current day needs to
// be advanced to next day).
// handle case#1
if (nextHour > currentHour) {
// set the chosen day of hour
nextCal.set(Calendar.HOUR_OF_DAY, nextHour);
// since we are moving to a different hour (as compared to the current hour),
// we should reset the second and minute appropriately, to their first possible
// values
nextCal.set(Calendar.SECOND, this.second.getFirst());
nextCal.set(Calendar.MINUTE, this.minute.getFirst());
return nextCal;
}
// case#2
if (nextHour < currentHour) {
// set the chosen hour
nextCal.set(Calendar.HOUR_OF_DAY, nextHour);
// since we are moving to a different hour (as compared to the current hour),
// we should reset the second and minute appropriately, to their first possible
// values
nextCal.set(Calendar.SECOND, this.second.getFirst());
nextCal.set(Calendar.MINUTE, this.minute.getFirst());
// advance to next day
nextCal.add(Calendar.DATE, 1);
return nextCal;
}
return null;
}
private Calendar computeNextDayOfWeek(Calendar currentCal) {
if (this.noMoreTimeouts(currentCal)) {
return null;
}
Integer nextDayOfWeek = this.dayOfWeek.getNextMatch(currentCal);
if (nextDayOfWeek == null) {
return null;
}
int currentDayOfWeek = currentCal.get(Calendar.DAY_OF_WEEK);
// if the current day-of-week is a match, then nothing else to
// do. Just return back the calendar
if (currentDayOfWeek == nextDayOfWeek) {
return currentCal;
}
Calendar nextCal = this.copy(currentCal);
// At this point, a suitable "next" day-of-week has been identified.
// There can be 2 cases
// 1) The "next" day-of-week is greater than the current day-of-week : This
// implies that the next day-of-week is within the "current" week.
// 2) The "next" day-of-week is lesser than the current day-of-week : This implies
// that the next day-of-week is in the next week (i.e. current week needs to
// be advanced to next week).
// handle case#1
if (nextDayOfWeek > currentDayOfWeek) {
// set the chosen day-of-week
int dayDiff = nextDayOfWeek - currentDayOfWeek;
nextCal.add(Calendar.DAY_OF_MONTH, dayDiff);
// since we are moving to a different day-of-week (as compared to the current day-of-week),
// we should reset the second, minute and hour appropriately, to their first possible
// values
nextCal.set(Calendar.SECOND, this.second.getFirst());
nextCal.set(Calendar.MINUTE, this.minute.getFirst());
nextCal.set(Calendar.HOUR_OF_DAY, this.hour.getFirst());
return nextCal;
}
// case#2
if (nextDayOfWeek < currentDayOfWeek) {
// set the chosen day-of-week
nextCal.set(Calendar.DAY_OF_WEEK, nextDayOfWeek);
// advance to next week
nextCal.add(Calendar.WEEK_OF_MONTH, 1);
// since we are moving to a different day-of-week (as compared to the current day-of-week),
// we should reset the second, minute and hour appropriately, to their first possible
// values
nextCal.set(Calendar.SECOND, this.second.getFirst());
nextCal.set(Calendar.MINUTE, this.minute.getFirst());
nextCal.set(Calendar.HOUR_OF_DAY, this.hour.getFirst());
return nextCal;
}
return null;
}
private Calendar computeNextMonth(Calendar currentCal) {
if (this.noMoreTimeouts(currentCal)) {
return null;
}
Integer nextMonth = this.month.getNextMatch(currentCal);
if (nextMonth == null) {
return null;
}
int currentMonth = currentCal.get(Calendar.MONTH);
// if the current month is a match, then nothing else to
// do. Just return back the calendar
if (currentMonth == nextMonth) {
return currentCal;
}
Calendar nextCal = this.copy(currentCal);
// At this point, a suitable "next" month has been identified.
// There can be 2 cases
// 1) The "next" month is greater than the current month : This
// implies that the next month is within the "current" year.
// 2) The "next" month is lesser than the current month : This implies
// that the next month is in the next year (i.e. current year needs to
// be advanced to next year).
// handle case#1
if (nextMonth > currentMonth) {
// since we are moving to a different month (as compared to the current month),
// we should reset the second, minute, hour, day-of-week and dayofmonth appropriately, to their first possible
// values
nextCal.set(Calendar.SECOND, this.second.getFirst());
nextCal.set(Calendar.MINUTE, this.minute.getFirst());
nextCal.set(Calendar.HOUR_OF_DAY, this.hour.getFirst());
nextCal.set(Calendar.DAY_OF_WEEK, this.dayOfWeek.getFirst());
nextCal.set(Calendar.DAY_OF_MONTH, 1);
// set the chosen month
nextCal.set(Calendar.MONTH, nextMonth);
return nextCal;
}
// case#2
if (nextMonth < currentMonth) {
// set the chosen month
nextCal.set(Calendar.MONTH, nextMonth);
// since we are moving to a different month (as compared to the current month),
// we should reset the second, minute, hour, day-of-week and dayofmonth appropriately, to their first possible
// values
nextCal.set(Calendar.SECOND, this.second.getFirst());
nextCal.set(Calendar.MINUTE, this.minute.getFirst());
nextCal.set(Calendar.HOUR_OF_DAY, this.hour.getFirst());
nextCal.set(Calendar.DAY_OF_WEEK, this.dayOfWeek.getFirst());
nextCal.set(Calendar.DAY_OF_MONTH, 1);
// advance to next year
nextCal.add(Calendar.YEAR, 1);
return nextCal;
}
return null;
}
private Calendar computeNextDate(Calendar currentCal) {
if (this.noMoreTimeouts(currentCal)) {
return null;
}
if (this.isDayOfMonthWildcard()) {
return this.computeNextDayOfWeek(currentCal);
}
if (this.isDayOfWeekWildcard()) {
return this.computeNextDayOfMonth(currentCal);
}
// both day-of-month and day-of-week are *non-wildcards*
Calendar nextDayOfMonthCal = this.computeNextDayOfMonth(currentCal);
Calendar nextDayOfWeekCal = this.computeNextDayOfWeek(currentCal);
if (nextDayOfMonthCal == null) {
return nextDayOfWeekCal;
}
if (nextDayOfWeekCal == null) {
return nextDayOfMonthCal;
}
return nextDayOfWeekCal.getTime().before(nextDayOfMonthCal.getTime()) ? nextDayOfWeekCal : nextDayOfMonthCal;
}
private Calendar computeNextDayOfMonth(Calendar currentCal) {
if (this.noMoreTimeouts(currentCal)) {
return null;
}
Integer nextDayOfMonth = this.dayOfMonth.getNextMatch(currentCal);
if (nextDayOfMonth == null) {
return null;
}
int currentDayOfMonth = currentCal.get(Calendar.DAY_OF_MONTH);
// if the current day-of-month is a match, then nothing else to
// do. Just return back the calendar
if (currentDayOfMonth == nextDayOfMonth) {
return currentCal;
}
Calendar nextCal = this.copy(currentCal);
if (nextDayOfMonth > currentDayOfMonth) {
if (this.monthHasDate(nextCal, nextDayOfMonth)) {
// set the chosen day-of-month
nextCal.set(Calendar.DAY_OF_MONTH, nextDayOfMonth);
// since we are moving to a different day-of-month (as compared to the current day-of-month),
// we should reset the second, minute and hour appropriately, to their first possible
// values
nextCal.set(Calendar.SECOND, this.second.getFirst());
nextCal.set(Calendar.MINUTE, this.minute.getFirst());
nextCal.set(Calendar.HOUR_OF_DAY, this.hour.getFirst());
} else {
nextCal = this.advanceTillMonthHasDate(nextCal, nextDayOfMonth);
}
} else if (nextDayOfMonth < currentDayOfMonth) {
nextCal.add(Calendar.MONTH, 1);
nextCal = this.computeNextMonth(nextCal);
if (nextCal == null) {
return null;
}
nextDayOfMonth = this.dayOfMonth.getFirstMatch(nextCal);
if (nextDayOfMonth == null) {
return null;
}
// make sure the month can handle the date
nextCal = this.advanceTillMonthHasDate(nextCal, nextDayOfMonth);
}
return nextCal;
}
private Calendar computeNextYear(Calendar currentCal) {
if (this.noMoreTimeouts(currentCal)) {
return null;
}
Integer nextYear = this.year.getNextMatch(currentCal);
if (nextYear == null || nextYear > Year.MAX_YEAR) {
return null;
}
int currentYear = currentCal.get(Calendar.YEAR);
// if the current year is a match, then nothing else to
// do. Just return back the calendar
if (currentYear == nextYear) {
return currentCal;
}
// If the next year is lesser than the current year, then
// we have no more timeouts for the calendar expression
if (nextYear < currentYear) {
return null;
}
Calendar nextCal = this.copy(currentCal);
// at this point we have chosen a year which is greater than the current
// year.
// set the chosen year
nextCal.set(Calendar.YEAR, nextYear);
// since we are moving to a different year (as compared to the current year),
// we should reset all other calendar attribute expressions appropriately, to their first possible
// values
nextCal.set(Calendar.SECOND, this.second.getFirst());
nextCal.set(Calendar.MINUTE, this.minute.getFirst());
nextCal.set(Calendar.HOUR_OF_DAY, this.hour.getFirst());
nextCal.set(Calendar.MONTH, this.month.getFirstMatch());
nextCal.set(Calendar.DAY_OF_MONTH, 1);
nextCal = this.computeNextDate(nextCal);
if (nextCal == null) {
return null;
}
return nextCal;
}
private Calendar advanceTillMonthHasDate(Calendar cal, Integer date) {
Calendar copy = this.copy(cal);
// make sure the month can handle the date
while (monthHasDate(copy, date) == false) {
if (copy.get(Calendar.YEAR) > Year.MAX_YEAR) {
return null;
}
// this month can't handle the date, so advance month to next month
// and get the next suitable matching month
copy.add(Calendar.MONTH, 1);
copy = this.computeNextMonth(copy);
if (copy == null) {
return null;
}
date = this.dayOfMonth.getFirstMatch(copy);
if (date == null) {
return null;
}
copy.set(Calendar.SECOND, this.second.getFirst());
copy.set(Calendar.MINUTE, this.minute.getFirst());
copy.set(Calendar.HOUR_OF_DAY, this.hour.getFirst());
}
copy.set(Calendar.DAY_OF_MONTH, date);
return copy;
}
private Calendar copy(Calendar cal) {
Calendar copy = new GregorianCalendar(cal.getTimeZone());
copy.setTime(cal.getTime());
return copy;
}
private boolean monthHasDate(Calendar cal, int date) {
Calendar tmpCal = new GregorianCalendar(cal.getTimeZone());
tmpCal.set(Calendar.YEAR, cal.get(Calendar.YEAR));
tmpCal.set(Calendar.MONTH, cal.get(Calendar.MONTH));
tmpCal.set(Calendar.DAY_OF_MONTH, 1);
int maximumPossibleDateForTheMonth = tmpCal.getActualMaximum(Calendar.DAY_OF_MONTH);
if (date > maximumPossibleDateForTheMonth) {
return false;
}
return true;
}
private boolean isAfterEnd(Calendar cal) {
Date end = this.scheduleExpression.getEnd();
if (end == null) {
return false;
}
// check that the next timeout isn't past the end date
return cal.getTime().after(end);
}
private boolean noMoreTimeouts(Calendar cal) {
if (cal.get(Calendar.YEAR) > Year.MAX_YEAR || isAfterEnd(cal)) {
return true;
}
return false;
}
private boolean isDayOfWeekWildcard() {
return this.scheduleExpression.getDayOfWeek().equals("*");
}
private boolean isDayOfMonthWildcard() {
return this.scheduleExpression.getDayOfMonth().equals("*");
}
private void nullCheckScheduleAttributes(ScheduleExpression schedule) {
if (schedule.getSecond() == null) {
throw MESSAGES.invalidScheduleExpressionSecond(schedule);
}
if (schedule.getMinute() == null) {
throw MESSAGES.invalidScheduleExpressionMinute(schedule);
}
if (schedule.getHour() == null) {
throw MESSAGES.invalidScheduleExpressionHour(schedule);
}
if (schedule.getDayOfMonth() == null) {
throw MESSAGES.invalidScheduleExpressionDayOfMonth(schedule);
}
if (schedule.getDayOfWeek() == null) {
throw MESSAGES.invalidScheduleExpressionDayOfWeek(schedule);
}
if (schedule.getMonth() == null) {
throw MESSAGES.invalidScheduleExpressionMonth(schedule);
}
if (schedule.getYear() == null) {
throw MESSAGES.invalidScheduleExpressionYear(schedule);
}
}
private ScheduleExpression clone(ScheduleExpression schedule) {
// clone the schedule
ScheduleExpression clonedSchedule = new ScheduleExpression();
clonedSchedule.second(schedule.getSecond());
clonedSchedule.minute(schedule.getMinute());
clonedSchedule.hour(schedule.getHour());
clonedSchedule.dayOfWeek(schedule.getDayOfWeek());
clonedSchedule.dayOfMonth(schedule.getDayOfMonth());
clonedSchedule.month(schedule.getMonth());
clonedSchedule.year(schedule.getYear());
clonedSchedule.timezone(schedule.getTimezone());
clonedSchedule.start(schedule.getStart());
clonedSchedule.end(schedule.getEnd());
return clonedSchedule;
}
}