diff --git a/README.md b/README.md index 3b8a585..d5572dc 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,22 @@ -# SimplyScheduled -Lightweight Cron-Like Scheduling Library for Java +# Simply Scheduled + +This is a very simple cron-like mechanism for scheduling tasks to execute at scheduled points in time. Here's an example: + +```java +Runnable job = () -> System.out.println("Doing some work..."); +Schedule schedule = new RepeatingSchedule(ChronoUnit.SECONDS, 5); +Scheduler scheduler = new BasicScheduler(); +scheduler.add(new Task(job, schedule)); +scheduler.start(); +``` +> In the above example, we create a new job that simply prints a string to standard output. We create a schedule that repeats the task every 5 seconds. Finally, we add the job and schedule together as a Task, add that task to a scheduler, and start the scheduler. + +Besides the `RepeatingSchedule`, this module also includes the following pre-made schedule implementations: + +* `DailySchedule` - Executes a task once per day, at a specified time. +* `HourlySchedule` - Executes a task once per hour, at a specified minute of the hour. +* `MinutelySchedule` - Executes a task once per minute, at a specified second. + +## Extensibility + +It is very simple to provide your own custom Schedule implementation if you want a more specific, fine-tuned schedule than what is provided here. Furthermore, you can also provide your own Scheduler implementation, if you prefer to use a different mechanism than the default dynamic thread pool executor service. diff --git a/src/main/java/nl/andrewlalis/simply_scheduled/BasicScheduler.java b/src/main/java/nl/andrewlalis/simply_scheduled/BasicScheduler.java index 96ab955..32df530 100644 --- a/src/main/java/nl/andrewlalis/simply_scheduled/BasicScheduler.java +++ b/src/main/java/nl/andrewlalis/simply_scheduled/BasicScheduler.java @@ -40,11 +40,12 @@ public class BasicScheduler extends Thread implements Scheduler { try { Task nextTask = this.tasks.take(); Instant now = this.clock.instant(); - long waitTime = nextTask.getSchedule().computeNextExecutionTime(now).toEpochMilli() - now.toEpochMilli(); + long waitTime = nextTask.getSchedule().getNextExecutionTime(now).toEpochMilli() - now.toEpochMilli(); if (waitTime > 0) { Thread.sleep(waitTime); } this.executorService.execute(nextTask.getRunnable()); + nextTask.getSchedule().markExecuted(this.clock.instant()); this.tasks.put(nextTask); // Put the task back in the queue. } catch (InterruptedException e) { this.setRunning(false); diff --git a/src/main/java/nl/andrewlalis/simply_scheduled/schedule/DailySchedule.java b/src/main/java/nl/andrewlalis/simply_scheduled/schedule/DailySchedule.java index 7213455..0a5d8cd 100644 --- a/src/main/java/nl/andrewlalis/simply_scheduled/schedule/DailySchedule.java +++ b/src/main/java/nl/andrewlalis/simply_scheduled/schedule/DailySchedule.java @@ -2,6 +2,10 @@ package nl.andrewlalis.simply_scheduled.schedule; import java.time.*; +/** + * A daily schedule plans for the execution of a task once per day, at a + * specified local time. + */ public class DailySchedule implements Schedule { private final ZoneId zoneId; private final LocalTime time; @@ -16,7 +20,7 @@ public class DailySchedule implements Schedule { } @Override - public Instant computeNextExecutionTime(Instant referenceInstant) { + public Instant getNextExecutionTime(Instant referenceInstant) { ZonedDateTime currentTime = referenceInstant.atZone(this.zoneId); LocalDate currentDay = LocalDate.from(referenceInstant); ZonedDateTime sameDayExecution = currentDay.atTime(this.time).atZone(this.zoneId); diff --git a/src/main/java/nl/andrewlalis/simply_scheduled/schedule/HourlySchedule.java b/src/main/java/nl/andrewlalis/simply_scheduled/schedule/HourlySchedule.java index c1a74d3..5d70ea4 100644 --- a/src/main/java/nl/andrewlalis/simply_scheduled/schedule/HourlySchedule.java +++ b/src/main/java/nl/andrewlalis/simply_scheduled/schedule/HourlySchedule.java @@ -5,6 +5,10 @@ import java.time.ZoneId; import java.time.ZonedDateTime; import java.time.temporal.ChronoUnit; +/** + * An hourly schedule is used to execute a task once per hour, at a specific + * minute of the hour. + */ public class HourlySchedule implements Schedule { private final ZoneId zoneId; private final int minute; @@ -22,7 +26,7 @@ public class HourlySchedule implements Schedule { } @Override - public Instant computeNextExecutionTime(Instant referenceInstant) { + public Instant getNextExecutionTime(Instant referenceInstant) { ZonedDateTime currentTime = referenceInstant.atZone(this.zoneId); int currentMinute = currentTime.getMinute(); if (currentMinute < this.minute) { diff --git a/src/main/java/nl/andrewlalis/simply_scheduled/schedule/MinutelySchedule.java b/src/main/java/nl/andrewlalis/simply_scheduled/schedule/MinutelySchedule.java index c029b5a..0afe259 100644 --- a/src/main/java/nl/andrewlalis/simply_scheduled/schedule/MinutelySchedule.java +++ b/src/main/java/nl/andrewlalis/simply_scheduled/schedule/MinutelySchedule.java @@ -20,7 +20,7 @@ public class MinutelySchedule implements Schedule { } @Override - public Instant computeNextExecutionTime(Instant referenceInstant) { + public Instant getNextExecutionTime(Instant referenceInstant) { ZonedDateTime currentTime = referenceInstant.atZone(this.zoneId); int currentSecond = currentTime.getSecond(); if (currentSecond >= this.second) { diff --git a/src/main/java/nl/andrewlalis/simply_scheduled/schedule/RepeatingSchedule.java b/src/main/java/nl/andrewlalis/simply_scheduled/schedule/RepeatingSchedule.java index ca721f6..284b381 100644 --- a/src/main/java/nl/andrewlalis/simply_scheduled/schedule/RepeatingSchedule.java +++ b/src/main/java/nl/andrewlalis/simply_scheduled/schedule/RepeatingSchedule.java @@ -30,12 +30,15 @@ public class RepeatingSchedule implements Schedule { * @return The next instant to execute the task at. */ @Override - public Instant computeNextExecutionTime(Instant referenceInstant) { + public Instant getNextExecutionTime(Instant referenceInstant) { if (this.lastExecution == null) { this.lastExecution = referenceInstant; } - Instant nextExecution = this.lastExecution.plus(multiple, unit); - this.lastExecution = nextExecution; - return nextExecution; + return this.lastExecution.plus(multiple, unit); + } + + @Override + public void markExecuted(Instant instant) { + this.lastExecution = instant; } } diff --git a/src/main/java/nl/andrewlalis/simply_scheduled/schedule/Schedule.java b/src/main/java/nl/andrewlalis/simply_scheduled/schedule/Schedule.java index ca50371..726ca78 100644 --- a/src/main/java/nl/andrewlalis/simply_scheduled/schedule/Schedule.java +++ b/src/main/java/nl/andrewlalis/simply_scheduled/schedule/Schedule.java @@ -14,14 +14,18 @@ public interface Schedule { * produce an instant sometime in the future at which the next execution of * a task should happen. * - *

- * Note that certain implementations may introduce side-effects - * when this method is called more than once. - *

- * * @param referenceInstant The instant representing the current time. * @return An instant in the future indicating the next time at which a task * using this schedule should be executed. */ - Instant computeNextExecutionTime(Instant referenceInstant); + Instant getNextExecutionTime(Instant referenceInstant); + + /** + * This method is called on the schedule as an indication that the scheduler + * should move on to planning the next execution time. + * @param instant The instant at which the schedule's task(s) were executed. + */ + default void markExecuted(Instant instant) { + // Default no-op. + } } diff --git a/src/main/java/nl/andrewlalis/simply_scheduled/schedule/Task.java b/src/main/java/nl/andrewlalis/simply_scheduled/schedule/Task.java index 79ae598..e0baf11 100644 --- a/src/main/java/nl/andrewlalis/simply_scheduled/schedule/Task.java +++ b/src/main/java/nl/andrewlalis/simply_scheduled/schedule/Task.java @@ -34,6 +34,6 @@ public class Task implements Comparable{ @Override public int compareTo(Task o) { Instant now = clock.instant(); - return this.schedule.computeNextExecutionTime(now).compareTo(o.getSchedule().computeNextExecutionTime(now)); + return this.schedule.getNextExecutionTime(now).compareTo(o.getSchedule().getNextExecutionTime(now)); } } diff --git a/src/test/java/nl/andrewlalis/simply_scheduled/SchedulerTest.java b/src/test/java/nl/andrewlalis/simply_scheduled/SchedulerTest.java index 867b54e..7635b6f 100644 --- a/src/test/java/nl/andrewlalis/simply_scheduled/SchedulerTest.java +++ b/src/test/java/nl/andrewlalis/simply_scheduled/SchedulerTest.java @@ -31,7 +31,7 @@ public class SchedulerTest { scheduler.addTask(task); scheduler.start(); System.out.println("Now: " + clock.instant().toString()); - System.out.println("Next task execution: " + task.getSchedule().computeNextExecutionTime(clock.instant())); + System.out.println("Next task execution: " + task.getSchedule().getNextExecutionTime(clock.instant())); System.out.printf("Waiting %d seconds for task to run...", secondsLeft); assertFalse(flag.get()); try {