Changed to nl.andrewl domain and added DailyScheduleTest
This commit is contained in:
parent
27dce051f7
commit
7438c3aa5b
71
pom.xml
71
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">
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
<groupId>nl.andrewlalis</groupId>
|
<groupId>nl.andrewl</groupId>
|
||||||
<artifactId>simply-scheduled</artifactId>
|
<artifactId>simply-scheduled</artifactId>
|
||||||
<version>1.0-SNAPSHOT</version>
|
<version>1.0-SNAPSHOT</version>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
|
@ -42,15 +42,16 @@
|
||||||
<java.version>11</java.version>
|
<java.version>11</java.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<build>
|
<distributionManagement>
|
||||||
<plugins>
|
<snapshotRepository>
|
||||||
<plugin>
|
<id>ossrh</id>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<url>https://s01.oss.sonatype.org/content/repositories/snapshots</url>
|
||||||
<artifactId>maven-surefire-plugin</artifactId>
|
</snapshotRepository>
|
||||||
<version>3.0.0-M5</version>
|
<repository>
|
||||||
</plugin>
|
<id>ossrh</id>
|
||||||
</plugins>
|
<url>https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/</url>
|
||||||
</build>
|
</repository>
|
||||||
|
</distributionManagement>
|
||||||
|
|
||||||
<dependencyManagement>
|
<dependencyManagement>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
@ -73,4 +74,54 @@
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-surefire-plugin</artifactId>
|
||||||
|
<version>3.0.0-M5</version>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-gpg-plugin</artifactId>
|
||||||
|
<version>3.0.1</version>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>sign-artifacts</id>
|
||||||
|
<phase>verify</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>sign</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-javadoc-plugin</artifactId>
|
||||||
|
<version>3.3.0</version>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>attach-javadocs</id>
|
||||||
|
<goals>
|
||||||
|
<goal>jar</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-source-plugin</artifactId>
|
||||||
|
<version>3.2.1</version>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>attach-sources</id>
|
||||||
|
<goals>
|
||||||
|
<goal>jar-no-fork</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
</project>
|
</project>
|
|
@ -1,6 +1,10 @@
|
||||||
|
/**
|
||||||
|
* The SimplyScheduled module.
|
||||||
|
*/
|
||||||
module simply_scheduled {
|
module simply_scheduled {
|
||||||
exports nl.andrewlalis.simply_scheduled;
|
exports nl.andrewl.simply_scheduled;
|
||||||
exports nl.andrewlalis.simply_scheduled.schedule;
|
exports nl.andrewl.simply_scheduled.schedule;
|
||||||
|
|
||||||
opens nl.andrewlalis.simply_scheduled;
|
// Needed for JUnit testing.
|
||||||
|
opens nl.andrewl.simply_scheduled;
|
||||||
}
|
}
|
|
@ -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.Clock;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
|
@ -20,12 +20,22 @@ public class BasicScheduler extends Thread implements Scheduler {
|
||||||
private final ExecutorService executorService;
|
private final ExecutorService executorService;
|
||||||
private boolean running = false;
|
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) {
|
public BasicScheduler(Clock clock, ExecutorService executorService) {
|
||||||
this.clock = clock;
|
this.clock = clock;
|
||||||
this.tasks = new PriorityBlockingQueue<>();
|
this.tasks = new PriorityBlockingQueue<>();
|
||||||
this.executorService = executorService;
|
this.executorService = executorService;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs the scheduler using the system's default clock, and a new
|
||||||
|
* work-stealing thread pool.
|
||||||
|
*/
|
||||||
public BasicScheduler() {
|
public BasicScheduler() {
|
||||||
this(Clock.systemDefaultZone(), Executors.newWorkStealingPool());
|
this(Clock.systemDefaultZone(), Executors.newWorkStealingPool());
|
||||||
}
|
}
|
|
@ -1,11 +1,14 @@
|
||||||
package nl.andrewlalis.simply_scheduled;
|
package nl.andrewl.simply_scheduled;
|
||||||
|
|
||||||
import nl.andrewlalis.simply_scheduled.schedule.RepeatingSchedule;
|
import nl.andrewl.simply_scheduled.schedule.RepeatingSchedule;
|
||||||
import nl.andrewlalis.simply_scheduled.schedule.Schedule;
|
import nl.andrewl.simply_scheduled.schedule.Schedule;
|
||||||
|
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.time.temporal.ChronoUnit;
|
import java.time.temporal.ChronoUnit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simple demonstration of scheduling functionality.
|
||||||
|
*/
|
||||||
public class Demo {
|
public class Demo {
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
Scheduler scheduler = new BasicScheduler();
|
Scheduler scheduler = new BasicScheduler();
|
|
@ -1,7 +1,7 @@
|
||||||
package nl.andrewlalis.simply_scheduled;
|
package nl.andrewl.simply_scheduled;
|
||||||
|
|
||||||
import nl.andrewlalis.simply_scheduled.schedule.Schedule;
|
import nl.andrewl.simply_scheduled.schedule.Task;
|
||||||
import nl.andrewlalis.simply_scheduled.schedule.Task;
|
import nl.andrewl.simply_scheduled.schedule.Schedule;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A scheduler is responsible for storing and executing tasks as defined by each
|
* A scheduler is responsible for storing and executing tasks as defined by each
|
|
@ -1,4 +1,4 @@
|
||||||
package nl.andrewlalis.simply_scheduled.schedule;
|
package nl.andrewl.simply_scheduled.schedule;
|
||||||
|
|
||||||
import java.time.*;
|
import java.time.*;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
@ -11,10 +11,21 @@ public class DailySchedule implements Schedule {
|
||||||
private final ZoneId zoneId;
|
private final ZoneId zoneId;
|
||||||
private final LocalTime time;
|
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) {
|
public DailySchedule(LocalTime time) {
|
||||||
this(time, ZoneId.systemDefault());
|
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) {
|
public DailySchedule(LocalTime time, ZoneId zoneId) {
|
||||||
this.time = time;
|
this.time = time;
|
||||||
this.zoneId = zoneId;
|
this.zoneId = zoneId;
|
||||||
|
@ -23,9 +34,9 @@ public class DailySchedule implements Schedule {
|
||||||
@Override
|
@Override
|
||||||
public Optional<Instant> getNextExecutionTime(Instant referenceInstant) {
|
public Optional<Instant> getNextExecutionTime(Instant referenceInstant) {
|
||||||
ZonedDateTime currentTime = referenceInstant.atZone(this.zoneId);
|
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);
|
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.toInstant());
|
||||||
}
|
}
|
||||||
return Optional.of(sameDayExecution.plusDays(1).toInstant());
|
return Optional.of(sameDayExecution.plusDays(1).toInstant());
|
|
@ -1,4 +1,4 @@
|
||||||
package nl.andrewlalis.simply_scheduled.schedule;
|
package nl.andrewl.simply_scheduled.schedule;
|
||||||
|
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.time.ZoneId;
|
import java.time.ZoneId;
|
|
@ -1,4 +1,4 @@
|
||||||
package nl.andrewlalis.simply_scheduled.schedule;
|
package nl.andrewl.simply_scheduled.schedule;
|
||||||
|
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.time.temporal.ChronoUnit;
|
import java.time.temporal.ChronoUnit;
|
|
@ -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.time.Instant;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
|
@ -1,4 +1,4 @@
|
||||||
package nl.andrewlalis.simply_scheduled.schedule;
|
package nl.andrewl.simply_scheduled.schedule;
|
||||||
|
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.time.ZonedDateTime;
|
import java.time.ZonedDateTime;
|
|
@ -1,4 +1,4 @@
|
||||||
package nl.andrewlalis.simply_scheduled.schedule;
|
package nl.andrewl.simply_scheduled.schedule;
|
||||||
|
|
||||||
import java.time.Clock;
|
import java.time.Clock;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
|
@ -52,6 +52,15 @@ public class Task implements Comparable<Task>{
|
||||||
return schedule;
|
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
|
@Override
|
||||||
public int compareTo(Task o) {
|
public int compareTo(Task o) {
|
||||||
Instant now = clock.instant();
|
Instant now = clock.instant();
|
|
@ -1,8 +1,8 @@
|
||||||
package nl.andrewlalis.simply_scheduled;
|
package nl.andrewl.simply_scheduled;
|
||||||
|
|
||||||
import nl.andrewlalis.simply_scheduled.schedule.RepeatingSchedule;
|
import nl.andrewl.simply_scheduled.schedule.Task;
|
||||||
import nl.andrewlalis.simply_scheduled.schedule.Schedule;
|
import nl.andrewl.simply_scheduled.schedule.RepeatingSchedule;
|
||||||
import nl.andrewlalis.simply_scheduled.schedule.Task;
|
import nl.andrewl.simply_scheduled.schedule.Schedule;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import java.time.Clock;
|
import java.time.Clock;
|
|
@ -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<TestCase> 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)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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<TestCase> getTestCases();
|
||||||
|
}
|
Loading…
Reference in New Issue