Added more to readme and javadoc.

This commit is contained in:
Andrew Lalis 2021-04-16 23:40:37 +02:00
parent 967dcfffe3
commit f2b2b8667c
9 changed files with 54 additions and 18 deletions

View File

@ -1,2 +1,22 @@
# Simply Scheduled # Simply Scheduled
Lightweight Cron-Like Scheduling Library for Java
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.

View File

@ -40,11 +40,12 @@ public class BasicScheduler extends Thread implements Scheduler {
try { try {
Task nextTask = this.tasks.take(); Task nextTask = this.tasks.take();
Instant now = this.clock.instant(); 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) { if (waitTime > 0) {
Thread.sleep(waitTime); Thread.sleep(waitTime);
} }
this.executorService.execute(nextTask.getRunnable()); this.executorService.execute(nextTask.getRunnable());
nextTask.getSchedule().markExecuted(this.clock.instant());
this.tasks.put(nextTask); // Put the task back in the queue. this.tasks.put(nextTask); // Put the task back in the queue.
} catch (InterruptedException e) { } catch (InterruptedException e) {
this.setRunning(false); this.setRunning(false);

View File

@ -2,6 +2,10 @@ package nl.andrewlalis.simply_scheduled.schedule;
import java.time.*; 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 { public class DailySchedule implements Schedule {
private final ZoneId zoneId; private final ZoneId zoneId;
private final LocalTime time; private final LocalTime time;
@ -16,7 +20,7 @@ public class DailySchedule implements Schedule {
} }
@Override @Override
public Instant computeNextExecutionTime(Instant referenceInstant) { public Instant getNextExecutionTime(Instant referenceInstant) {
ZonedDateTime currentTime = referenceInstant.atZone(this.zoneId); ZonedDateTime currentTime = referenceInstant.atZone(this.zoneId);
LocalDate currentDay = LocalDate.from(referenceInstant); LocalDate currentDay = LocalDate.from(referenceInstant);
ZonedDateTime sameDayExecution = currentDay.atTime(this.time).atZone(this.zoneId); ZonedDateTime sameDayExecution = currentDay.atTime(this.time).atZone(this.zoneId);

View File

@ -5,6 +5,10 @@ import java.time.ZoneId;
import java.time.ZonedDateTime; import java.time.ZonedDateTime;
import java.time.temporal.ChronoUnit; 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 { public class HourlySchedule implements Schedule {
private final ZoneId zoneId; private final ZoneId zoneId;
private final int minute; private final int minute;
@ -22,7 +26,7 @@ public class HourlySchedule implements Schedule {
} }
@Override @Override
public Instant computeNextExecutionTime(Instant referenceInstant) { public Instant getNextExecutionTime(Instant referenceInstant) {
ZonedDateTime currentTime = referenceInstant.atZone(this.zoneId); ZonedDateTime currentTime = referenceInstant.atZone(this.zoneId);
int currentMinute = currentTime.getMinute(); int currentMinute = currentTime.getMinute();
if (currentMinute < this.minute) { if (currentMinute < this.minute) {

View File

@ -20,7 +20,7 @@ public class MinutelySchedule implements Schedule {
} }
@Override @Override
public Instant computeNextExecutionTime(Instant referenceInstant) { public Instant getNextExecutionTime(Instant referenceInstant) {
ZonedDateTime currentTime = referenceInstant.atZone(this.zoneId); ZonedDateTime currentTime = referenceInstant.atZone(this.zoneId);
int currentSecond = currentTime.getSecond(); int currentSecond = currentTime.getSecond();
if (currentSecond >= this.second) { if (currentSecond >= this.second) {

View File

@ -30,12 +30,15 @@ public class RepeatingSchedule implements Schedule {
* @return The next instant to execute the task at. * @return The next instant to execute the task at.
*/ */
@Override @Override
public Instant computeNextExecutionTime(Instant referenceInstant) { public Instant getNextExecutionTime(Instant referenceInstant) {
if (this.lastExecution == null) { if (this.lastExecution == null) {
this.lastExecution = referenceInstant; this.lastExecution = referenceInstant;
} }
Instant nextExecution = this.lastExecution.plus(multiple, unit); return this.lastExecution.plus(multiple, unit);
this.lastExecution = nextExecution; }
return nextExecution;
@Override
public void markExecuted(Instant instant) {
this.lastExecution = instant;
} }
} }

View File

@ -14,14 +14,18 @@ public interface Schedule {
* produce an instant sometime in the future at which the next execution of * produce an instant sometime in the future at which the next execution of
* a task should happen. * a task should happen.
* *
* <p>
* <strong>Note that certain implementations may introduce side-effects
* when this method is called more than once.</strong>
* </p>
*
* @param referenceInstant The instant representing the current time. * @param referenceInstant The instant representing the current time.
* @return An instant in the future indicating the next time at which a task * @return An instant in the future indicating the next time at which a task
* using this schedule should be executed. * 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.
}
} }

View File

@ -34,6 +34,6 @@ public class Task implements Comparable<Task>{
@Override @Override
public int compareTo(Task o) { public int compareTo(Task o) {
Instant now = clock.instant(); Instant now = clock.instant();
return this.schedule.computeNextExecutionTime(now).compareTo(o.getSchedule().computeNextExecutionTime(now)); return this.schedule.getNextExecutionTime(now).compareTo(o.getSchedule().getNextExecutionTime(now));
} }
} }

View File

@ -31,7 +31,7 @@ public class SchedulerTest {
scheduler.addTask(task); scheduler.addTask(task);
scheduler.start(); scheduler.start();
System.out.println("Now: " + clock.instant().toString()); 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); System.out.printf("Waiting %d seconds for task to run...", secondsLeft);
assertFalse(flag.get()); assertFalse(flag.get());
try { try {