diff --git a/pom.xml b/pom.xml index 0adf674..28528bf 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 - nl.andrewlalis + nl.andrewl simply-scheduled 1.0-SNAPSHOT jar @@ -42,15 +42,16 @@ 11 - - - - org.apache.maven.plugins - maven-surefire-plugin - 3.0.0-M5 - - - + + + ossrh + https://s01.oss.sonatype.org/content/repositories/snapshots + + + ossrh + https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/ + + @@ -73,4 +74,54 @@ test + + + + + org.apache.maven.plugins + maven-surefire-plugin + 3.0.0-M5 + + + org.apache.maven.plugins + maven-gpg-plugin + 3.0.1 + + + sign-artifacts + verify + + sign + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + 3.3.0 + + + attach-javadocs + + jar + + + + + + org.apache.maven.plugins + maven-source-plugin + 3.2.1 + + + attach-sources + + jar-no-fork + + + + + + \ No newline at end of file diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index 5735d78..1359d8f 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -1,6 +1,10 @@ +/** + * The SimplyScheduled module. + */ module simply_scheduled { - exports nl.andrewlalis.simply_scheduled; - exports nl.andrewlalis.simply_scheduled.schedule; + exports nl.andrewl.simply_scheduled; + exports nl.andrewl.simply_scheduled.schedule; - opens nl.andrewlalis.simply_scheduled; + // Needed for JUnit testing. + opens nl.andrewl.simply_scheduled; } \ No newline at end of file diff --git a/src/main/java/nl/andrewlalis/simply_scheduled/BasicScheduler.java b/src/main/java/nl/andrewl/simply_scheduled/BasicScheduler.java similarity index 85% rename from src/main/java/nl/andrewlalis/simply_scheduled/BasicScheduler.java rename to src/main/java/nl/andrewl/simply_scheduled/BasicScheduler.java index 49517e1..ecb516f 100644 --- a/src/main/java/nl/andrewlalis/simply_scheduled/BasicScheduler.java +++ b/src/main/java/nl/andrewl/simply_scheduled/BasicScheduler.java @@ -1,6 +1,6 @@ -package nl.andrewlalis.simply_scheduled; +package nl.andrewl.simply_scheduled; -import nl.andrewlalis.simply_scheduled.schedule.Task; +import nl.andrewl.simply_scheduled.schedule.Task; import java.time.Clock; import java.time.Instant; @@ -20,12 +20,22 @@ public class BasicScheduler extends Thread implements Scheduler { private final ExecutorService executorService; private boolean running = false; + /** + * Constructs the scheduler using the given clock and executor service. This + * constructor is most useful for test cases where a custom clock is used. + * @param clock The clock to use. + * @param executorService The executor service to use. + */ public BasicScheduler(Clock clock, ExecutorService executorService) { this.clock = clock; this.tasks = new PriorityBlockingQueue<>(); this.executorService = executorService; } + /** + * Constructs the scheduler using the system's default clock, and a new + * work-stealing thread pool. + */ public BasicScheduler() { this(Clock.systemDefaultZone(), Executors.newWorkStealingPool()); } diff --git a/src/main/java/nl/andrewlalis/simply_scheduled/Demo.java b/src/main/java/nl/andrewl/simply_scheduled/Demo.java similarity index 67% rename from src/main/java/nl/andrewlalis/simply_scheduled/Demo.java rename to src/main/java/nl/andrewl/simply_scheduled/Demo.java index bab9205..d92627d 100644 --- a/src/main/java/nl/andrewlalis/simply_scheduled/Demo.java +++ b/src/main/java/nl/andrewl/simply_scheduled/Demo.java @@ -1,11 +1,14 @@ -package nl.andrewlalis.simply_scheduled; +package nl.andrewl.simply_scheduled; -import nl.andrewlalis.simply_scheduled.schedule.RepeatingSchedule; -import nl.andrewlalis.simply_scheduled.schedule.Schedule; +import nl.andrewl.simply_scheduled.schedule.RepeatingSchedule; +import nl.andrewl.simply_scheduled.schedule.Schedule; import java.time.Instant; import java.time.temporal.ChronoUnit; +/** + * Simple demonstration of scheduling functionality. + */ public class Demo { public static void main(String[] args) { Scheduler scheduler = new BasicScheduler(); diff --git a/src/main/java/nl/andrewlalis/simply_scheduled/Scheduler.java b/src/main/java/nl/andrewl/simply_scheduled/Scheduler.java similarity index 88% rename from src/main/java/nl/andrewlalis/simply_scheduled/Scheduler.java rename to src/main/java/nl/andrewl/simply_scheduled/Scheduler.java index ff253b3..5796935 100644 --- a/src/main/java/nl/andrewlalis/simply_scheduled/Scheduler.java +++ b/src/main/java/nl/andrewl/simply_scheduled/Scheduler.java @@ -1,7 +1,7 @@ -package nl.andrewlalis.simply_scheduled; +package nl.andrewl.simply_scheduled; -import nl.andrewlalis.simply_scheduled.schedule.Schedule; -import nl.andrewlalis.simply_scheduled.schedule.Task; +import nl.andrewl.simply_scheduled.schedule.Task; +import nl.andrewl.simply_scheduled.schedule.Schedule; /** * A scheduler is responsible for storing and executing tasks as defined by each diff --git a/src/main/java/nl/andrewlalis/simply_scheduled/schedule/DailySchedule.java b/src/main/java/nl/andrewl/simply_scheduled/schedule/DailySchedule.java similarity index 57% rename from src/main/java/nl/andrewlalis/simply_scheduled/schedule/DailySchedule.java rename to src/main/java/nl/andrewl/simply_scheduled/schedule/DailySchedule.java index 9e61d60..5b4fc0b 100644 --- a/src/main/java/nl/andrewlalis/simply_scheduled/schedule/DailySchedule.java +++ b/src/main/java/nl/andrewl/simply_scheduled/schedule/DailySchedule.java @@ -1,4 +1,4 @@ -package nl.andrewlalis.simply_scheduled.schedule; +package nl.andrewl.simply_scheduled.schedule; import java.time.*; import java.util.Optional; @@ -11,10 +11,21 @@ public class DailySchedule implements Schedule { private final ZoneId zoneId; private final LocalTime time; + /** + * Constructs a new schedule that will execute at the given time, using the + * system's default time zone. + * @param time The time at which to execute any tasks using this schedule. + */ public DailySchedule(LocalTime time) { this(time, ZoneId.systemDefault()); } + /** + * Constructs a new schedule that will execute at the given time, using the + * given zone id for time zone information. + * @param time The time at which to execute any tasks using this schedule. + * @param zoneId The time zone id. + */ public DailySchedule(LocalTime time, ZoneId zoneId) { this.time = time; this.zoneId = zoneId; @@ -23,9 +34,9 @@ public class DailySchedule implements Schedule { @Override public Optional getNextExecutionTime(Instant referenceInstant) { ZonedDateTime currentTime = referenceInstant.atZone(this.zoneId); - LocalDate currentDay = LocalDate.from(referenceInstant); + LocalDate currentDay = LocalDate.ofInstant(referenceInstant, this.zoneId); ZonedDateTime sameDayExecution = currentDay.atTime(this.time).atZone(this.zoneId); - if (sameDayExecution.isBefore(currentTime)) { + if (sameDayExecution.isAfter(currentTime)) { return Optional.of(sameDayExecution.toInstant()); } return Optional.of(sameDayExecution.plusDays(1).toInstant()); diff --git a/src/main/java/nl/andrewlalis/simply_scheduled/schedule/HourlySchedule.java b/src/main/java/nl/andrewl/simply_scheduled/schedule/HourlySchedule.java similarity index 95% rename from src/main/java/nl/andrewlalis/simply_scheduled/schedule/HourlySchedule.java rename to src/main/java/nl/andrewl/simply_scheduled/schedule/HourlySchedule.java index 5db9b1e..720a2c6 100644 --- a/src/main/java/nl/andrewlalis/simply_scheduled/schedule/HourlySchedule.java +++ b/src/main/java/nl/andrewl/simply_scheduled/schedule/HourlySchedule.java @@ -1,4 +1,4 @@ -package nl.andrewlalis.simply_scheduled.schedule; +package nl.andrewl.simply_scheduled.schedule; import java.time.Instant; import java.time.ZoneId; diff --git a/src/main/java/nl/andrewlalis/simply_scheduled/schedule/RepeatingSchedule.java b/src/main/java/nl/andrewl/simply_scheduled/schedule/RepeatingSchedule.java similarity index 97% rename from src/main/java/nl/andrewlalis/simply_scheduled/schedule/RepeatingSchedule.java rename to src/main/java/nl/andrewl/simply_scheduled/schedule/RepeatingSchedule.java index 1f1cb8c..69b292d 100644 --- a/src/main/java/nl/andrewlalis/simply_scheduled/schedule/RepeatingSchedule.java +++ b/src/main/java/nl/andrewl/simply_scheduled/schedule/RepeatingSchedule.java @@ -1,4 +1,4 @@ -package nl.andrewlalis.simply_scheduled.schedule; +package nl.andrewl.simply_scheduled.schedule; import java.time.Instant; import java.time.temporal.ChronoUnit; diff --git a/src/main/java/nl/andrewlalis/simply_scheduled/schedule/Schedule.java b/src/main/java/nl/andrewl/simply_scheduled/schedule/Schedule.java similarity index 93% rename from src/main/java/nl/andrewlalis/simply_scheduled/schedule/Schedule.java rename to src/main/java/nl/andrewl/simply_scheduled/schedule/Schedule.java index e1b5a5c..e07accb 100644 --- a/src/main/java/nl/andrewlalis/simply_scheduled/schedule/Schedule.java +++ b/src/main/java/nl/andrewl/simply_scheduled/schedule/Schedule.java @@ -1,6 +1,6 @@ -package nl.andrewlalis.simply_scheduled.schedule; +package nl.andrewl.simply_scheduled.schedule; -import nl.andrewlalis.simply_scheduled.Scheduler; +import nl.andrewl.simply_scheduled.Scheduler; import java.time.Instant; import java.util.Optional; diff --git a/src/main/java/nl/andrewlalis/simply_scheduled/schedule/SpecificInstantSchedule.java b/src/main/java/nl/andrewl/simply_scheduled/schedule/SpecificInstantSchedule.java similarity index 95% rename from src/main/java/nl/andrewlalis/simply_scheduled/schedule/SpecificInstantSchedule.java rename to src/main/java/nl/andrewl/simply_scheduled/schedule/SpecificInstantSchedule.java index 57b3bc9..10e6068 100644 --- a/src/main/java/nl/andrewlalis/simply_scheduled/schedule/SpecificInstantSchedule.java +++ b/src/main/java/nl/andrewl/simply_scheduled/schedule/SpecificInstantSchedule.java @@ -1,4 +1,4 @@ -package nl.andrewlalis.simply_scheduled.schedule; +package nl.andrewl.simply_scheduled.schedule; import java.time.Instant; import java.time.ZonedDateTime; diff --git a/src/main/java/nl/andrewlalis/simply_scheduled/schedule/Task.java b/src/main/java/nl/andrewl/simply_scheduled/schedule/Task.java similarity index 78% rename from src/main/java/nl/andrewlalis/simply_scheduled/schedule/Task.java rename to src/main/java/nl/andrewl/simply_scheduled/schedule/Task.java index b563a81..381bd15 100644 --- a/src/main/java/nl/andrewlalis/simply_scheduled/schedule/Task.java +++ b/src/main/java/nl/andrewl/simply_scheduled/schedule/Task.java @@ -1,4 +1,4 @@ -package nl.andrewlalis.simply_scheduled.schedule; +package nl.andrewl.simply_scheduled.schedule; import java.time.Clock; import java.time.Instant; @@ -52,6 +52,15 @@ public class Task implements Comparable{ return schedule; } + /** + * Compares this task to another. This imposes a natural ordering of tasks + * according to their schedule's next planned execution time, such that + * tasks are ordered starting with those with the nearest execution time, to + * those whose execution time is further in the future. + * @param o The task to compare to. + * @return -1 if this task's next execution time is before the other task's, + * 1 if this task's next execution time is after the other, and 0 otherwise. + */ @Override public int compareTo(Task o) { Instant now = clock.instant(); diff --git a/src/test/java/nl/andrewlalis/simply_scheduled/BasicSchedulerTest.java b/src/test/java/nl/andrewl/simply_scheduled/BasicSchedulerTest.java similarity index 91% rename from src/test/java/nl/andrewlalis/simply_scheduled/BasicSchedulerTest.java rename to src/test/java/nl/andrewl/simply_scheduled/BasicSchedulerTest.java index 7f7f478..67e3418 100644 --- a/src/test/java/nl/andrewlalis/simply_scheduled/BasicSchedulerTest.java +++ b/src/test/java/nl/andrewl/simply_scheduled/BasicSchedulerTest.java @@ -1,8 +1,8 @@ -package nl.andrewlalis.simply_scheduled; +package nl.andrewl.simply_scheduled; -import nl.andrewlalis.simply_scheduled.schedule.RepeatingSchedule; -import nl.andrewlalis.simply_scheduled.schedule.Schedule; -import nl.andrewlalis.simply_scheduled.schedule.Task; +import nl.andrewl.simply_scheduled.schedule.Task; +import nl.andrewl.simply_scheduled.schedule.RepeatingSchedule; +import nl.andrewl.simply_scheduled.schedule.Schedule; import org.junit.jupiter.api.Test; import java.time.Clock; diff --git a/src/test/java/nl/andrewl/simply_scheduled/schedule/DailyScheduleTest.java b/src/test/java/nl/andrewl/simply_scheduled/schedule/DailyScheduleTest.java new file mode 100644 index 0000000..1cc18bd --- /dev/null +++ b/src/test/java/nl/andrewl/simply_scheduled/schedule/DailyScheduleTest.java @@ -0,0 +1,40 @@ +package nl.andrewl.simply_scheduled.schedule; + +import java.time.*; +import java.util.stream.Stream; + +/** + * Tests the ability of the {@link DailySchedule} to reliably give the correct + * next execution time. + */ +public class DailyScheduleTest extends ScheduleTest { + @Override + protected Stream getTestCases() { + var utc = ZoneOffset.UTC; + // For this test, we use a fixed clock at 12:30:45 on August 6, 2021, UTC. + Clock clock = Clock.fixed(ZonedDateTime.of(2021, 8, 6, 12, 30, 45, 0, utc).toInstant(), utc); + ZonedDateTime time = ZonedDateTime.ofInstant(clock.instant(), utc); + return Stream.of( + new TestCase( // A daily schedule whose time has already passed will be scheduled for tomorrow. + new DailySchedule(LocalTime.of(12, 0), utc), + clock.instant(), + time.plusDays(1).toLocalDate().atTime(12, 0).toInstant(utc) + ), + new TestCase( // A daily schedule whose time has not yet passed will be scheduled for today. + new DailySchedule(LocalTime.of(18, 44, 3), utc), + clock.instant(), + time.toLocalDate().atTime(18, 44, 3).toInstant(utc) + ), + new TestCase( // Account for a time zone which introduces some offset. + new DailySchedule(LocalTime.of(10, 30), ZoneOffset.ofHours(-5)), + clock.instant(), + time.toLocalDate().atTime(15, 30).toInstant(utc) + ), + new TestCase( // Account for a time zone whose offset makes it such that the next event is scheduled for tomorrow, in UTC. + new DailySchedule(LocalTime.of(10, 30), ZoneOffset.ofHours(-1)), + clock.instant(), + time.plusDays(1).toLocalDate().atTime(11, 30).toInstant(utc) + ) + ); + } +} diff --git a/src/test/java/nl/andrewl/simply_scheduled/schedule/ScheduleTest.java b/src/test/java/nl/andrewl/simply_scheduled/schedule/ScheduleTest.java new file mode 100644 index 0000000..0ddc35d --- /dev/null +++ b/src/test/java/nl/andrewl/simply_scheduled/schedule/ScheduleTest.java @@ -0,0 +1,43 @@ +package nl.andrewl.simply_scheduled.schedule; + +import org.junit.jupiter.api.Test; + +import java.time.Instant; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * Abstract test class which can be extended to test the functionality of a + * particular schedule's {@link Schedule#getNextExecutionTime(Instant)} method. + */ +public abstract class ScheduleTest { + protected static class TestCase { + public final Schedule schedule; + public final Instant referenceInstant; + public final Instant expectedNextExecution; + public TestCase(Schedule schedule, Instant referenceInstant, Instant expectedNextExecution) { + this.schedule = schedule; + this.referenceInstant = referenceInstant; + this.expectedNextExecution = expectedNextExecution; + } + } + + @Test + public void testGetNextExecutionTime() { + var cases = getTestCases().collect(Collectors.toList()); + for (int i = 0; i < cases.size(); i++) { + var testCase = cases.get(i); + var r = testCase.schedule.getNextExecutionTime(testCase.referenceInstant); + if (testCase.expectedNextExecution == null) { + assertTrue(r.isEmpty(), "Case " + i + ": next execution time is not empty when it should be."); + } else { + assertTrue(r.isPresent(), "Case " + i + ": next execution time is not present when it should be."); + assertEquals(testCase.expectedNextExecution, r.get(), "Case " + i + ": expected next execution time does not match expected."); + } + } + } + + protected abstract Stream getTestCases(); +}