From 3c9eed1c22ded2b42467ef253f9c8f0502e31d62 Mon Sep 17 00:00:00 2001 From: David Syer Date: Mon, 26 Oct 2009 22:37:04 +0000 Subject: [PATCH] SPR-6273: resolved with patch - use recursive call to reset low order bits and then re-seek match for pattern --- .../support/CronSequenceGenerator.java | 56 ++++++++++++++---- .../scheduling/support/CronTriggerTests.java | 59 +++++++++++++++++-- 2 files changed, 97 insertions(+), 18 deletions(-) diff --git a/org.springframework.context/src/main/java/org/springframework/scheduling/support/CronSequenceGenerator.java b/org.springframework.context/src/main/java/org/springframework/scheduling/support/CronSequenceGenerator.java index 8c2da44614..7b1f436689 100644 --- a/org.springframework.context/src/main/java/org/springframework/scheduling/support/CronSequenceGenerator.java +++ b/org.springframework.context/src/main/java/org/springframework/scheduling/support/CronSequenceGenerator.java @@ -66,7 +66,6 @@ class CronSequenceGenerator { private final String expression; - /** * Construct a {@link CronSequenceGenerator} from the pattern provided. * @param expression a space-separated list of time fields @@ -77,7 +76,6 @@ class CronSequenceGenerator { parse(expression); } - /** * Get the next {@link Date} in the sequence matching the Cron pattern and * after the value provided. The return value will have a whole number of @@ -86,6 +84,27 @@ class CronSequenceGenerator { * @return the next value matching the pattern */ public Date next(Date date) { + + /* + The plan: + + 1 Round up to the next whole second + + 2 If seconds match move on, otherwise find the next match: + 2.1 If next match is in the next minute then roll forwards + + 3 If minute matches move on, otherwise find the next match + 3.1 If next match is in the next hour then roll forwards + 3.2 Reset the seconds and go to 2 + + 4 If hour matches move on, otherwise find the next match + 4.1 If next match is in the next day then roll forwards, + 4.2 Reset the minutes and seconds and go to 2 + + ... + + */ + Calendar calendar = new GregorianCalendar(); calendar.setTime(date); @@ -93,10 +112,17 @@ class CronSequenceGenerator { calendar.add(Calendar.SECOND, 1); calendar.set(Calendar.MILLISECOND, 0); + doNext(calendar); + + return calendar.getTime(); + } + + private void doNext(Calendar calendar) { List resets = new ArrayList(); int second = calendar.get(Calendar.SECOND); - int updateSecond = findNext(this.seconds, second, 60, calendar, Calendar.SECOND, Collections. emptyList()); + List emptyList = Collections. emptyList(); + int updateSecond = findNext(this.seconds, second, 60, calendar, Calendar.SECOND, emptyList); if (second == updateSecond) { resets.add(Calendar.SECOND); } @@ -105,12 +131,16 @@ class CronSequenceGenerator { int updateMinute = findNext(this.minutes, minute, 60, calendar, Calendar.MINUTE, resets); if (minute == updateMinute) { resets.add(Calendar.MINUTE); + } else { + doNext(calendar); } int hour = calendar.get(Calendar.HOUR_OF_DAY); int updateHour = findNext(this.hours, hour, 24, calendar, Calendar.HOUR_OF_DAY, resets); if (hour == updateHour) { resets.add(Calendar.HOUR_OF_DAY); + } else { + doNext(calendar); } int dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK); @@ -118,16 +148,20 @@ class CronSequenceGenerator { int updateDayOfMonth = findNextDay(calendar, this.daysOfMonth, dayOfMonth, daysOfWeek, dayOfWeek, 366, resets); if (dayOfMonth == updateDayOfMonth) { resets.add(Calendar.DAY_OF_MONTH); + } else { + doNext(calendar); } int month = calendar.get(Calendar.MONTH); - month = findNext(this.months, month, 12, calendar, Calendar.MONTH, resets); + int updateMonth = findNext(this.months, month, 12, calendar, Calendar.MONTH, resets); + if (month != updateMonth) { + doNext(calendar); + } - return calendar.getTime(); } - private int findNextDay(Calendar calendar, BitSet daysOfMonth, int dayOfMonth, BitSet daysOfWeek, - int dayOfWeek, int max, List resets) { + private int findNextDay(Calendar calendar, BitSet daysOfMonth, int dayOfMonth, BitSet daysOfWeek, int dayOfWeek, + int max, List resets) { int count = 0; // the DAY_OF_WEEK values in java.util.Calendar start with 1 (Sunday), @@ -180,7 +214,6 @@ class CronSequenceGenerator { } } - // Parsing logic invoked by the constructor. /** @@ -240,8 +273,7 @@ class CronSequenceGenerator { // Not an incrementer so it must be a range (possibly empty) int[] range = getRange(field, max); bits.set(range[0], range[1] + 1); - } - else { + } else { String[] split = StringUtils.delimitedListToStringArray(field, "/"); if (split.length > 2) { throw new IllegalArgumentException("Incrementer has more than two fields: " + field); @@ -267,8 +299,7 @@ class CronSequenceGenerator { } if (!field.contains("-")) { result[0] = result[1] = Integer.valueOf(field); - } - else { + } else { String[] split = StringUtils.delimitedListToStringArray(field, "-"); if (split.length > 2) { throw new IllegalArgumentException("Range has more than two fields: " + field); @@ -279,7 +310,6 @@ class CronSequenceGenerator { return result; } - @Override public boolean equals(Object obj) { if (!(obj instanceof CronSequenceGenerator)) { diff --git a/org.springframework.context/src/test/java/org/springframework/scheduling/support/CronTriggerTests.java b/org.springframework.context/src/test/java/org/springframework/scheduling/support/CronTriggerTests.java index 33f3e4a089..b56ee85e1c 100644 --- a/org.springframework.context/src/test/java/org/springframework/scheduling/support/CronTriggerTests.java +++ b/org.springframework.context/src/test/java/org/springframework/scheduling/support/CronTriggerTests.java @@ -23,7 +23,6 @@ import java.util.Date; import java.util.GregorianCalendar; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; import org.springframework.scheduling.TriggerContext; @@ -316,20 +315,36 @@ public class CronTriggerTests { } @Test - @Ignore public void testSpecificMinuteSecond() throws Exception { - CronTrigger trigger = new CronTrigger("2 5 * * * *"); + CronTrigger trigger = new CronTrigger("55 5 * * * *"); calendar.set(Calendar.MINUTE, 4); + calendar.set(Calendar.SECOND, 54); Date date = calendar.getTime(); - calendar.add(Calendar.MINUTE, 1); - calendar.set(Calendar.SECOND, 2); TriggerContext context1 = getTriggerContext(date); + calendar.add(Calendar.MINUTE, 1); + calendar.set(Calendar.SECOND, 55); assertEquals(calendar.getTime(), date = trigger.nextExecutionTime(context1)); calendar.add(Calendar.HOUR, 1); TriggerContext context2 = getTriggerContext(date); assertEquals(calendar.getTime(), date = trigger.nextExecutionTime(context2)); } + @Test + public void testSpecificHourSecond() throws Exception { + CronTrigger trigger = new CronTrigger("55 * 2 * * *"); + calendar.set(Calendar.HOUR_OF_DAY, 1); + calendar.set(Calendar.SECOND, 54); + Date date = calendar.getTime(); + TriggerContext context1 = getTriggerContext(date); + calendar.add(Calendar.HOUR_OF_DAY, 1); + calendar.set(Calendar.MINUTE, 0); + calendar.set(Calendar.SECOND, 55); + assertEquals(calendar.getTime(), date = trigger.nextExecutionTime(context1)); + calendar.add(Calendar.MINUTE, 1); + TriggerContext context2 = getTriggerContext(date); + assertEquals(calendar.getTime(), date = trigger.nextExecutionTime(context2)); + } + @Test public void testSpecificMinuteHour() throws Exception { CronTrigger trigger = new CronTrigger("* 5 10 * * *"); @@ -347,6 +362,40 @@ public class CronTriggerTests { assertEquals(calendar.getTime(), date = trigger.nextExecutionTime(context2)); } + @Test + public void testSpecificDayOfMonthSecond() throws Exception { + CronTrigger trigger = new CronTrigger("55 * * 3 * *"); + calendar.set(Calendar.DAY_OF_MONTH, 2); + calendar.set(Calendar.SECOND, 54); + Date date = calendar.getTime(); + TriggerContext context1 = getTriggerContext(date); + calendar.add(Calendar.DAY_OF_MONTH, 1); + calendar.set(Calendar.HOUR_OF_DAY, 0); + calendar.set(Calendar.MINUTE, 0); + calendar.set(Calendar.SECOND, 55); + assertEquals(calendar.getTime(), date = trigger.nextExecutionTime(context1)); + calendar.add(Calendar.MINUTE, 1); + TriggerContext context2 = getTriggerContext(date); + assertEquals(calendar.getTime(), date = trigger.nextExecutionTime(context2)); + } + + @Test + public void testSpecificDate() throws Exception { + CronTrigger trigger = new CronTrigger("* * * 3 10 *"); + calendar.set(Calendar.DAY_OF_MONTH, 2); + calendar.set(Calendar.MONTH, 10); + Date date = calendar.getTime(); + TriggerContext context1 = getTriggerContext(date); + calendar.add(Calendar.DAY_OF_MONTH, 1); + calendar.set(Calendar.HOUR_OF_DAY, 0); + calendar.set(Calendar.MINUTE, 0); + calendar.set(Calendar.SECOND, 0); + assertEquals(calendar.getTime(), date = trigger.nextExecutionTime(context1)); + calendar.add(Calendar.SECOND, 1); + TriggerContext context2 = getTriggerContext(date); + assertEquals(calendar.getTime(), date = trigger.nextExecutionTime(context2)); + } + @Test public void testWeekDaySequence() throws Exception { CronTrigger trigger = new CronTrigger("0 0 7 ? * MON-FRI");