package com.coreoz.wisp.schedule.cron; import java.time.Instant; import java.time.ZoneId; import java.time.ZonedDateTime; import java.util.Locale; import com.coreoz.wisp.schedule.Schedule; import com.cronutils.descriptor.CronDescriptor; import com.cronutils.model.Cron; import com.cronutils.model.CronType; import com.cronutils.model.definition.CronDefinitionBuilder; import com.cronutils.model.time.ExecutionTime; import com.cronutils.parser.CronParser; /** * A {@link Schedule} based on a <a href="https://en.wikipedia.org/wiki/Cron#CRON_expression"> * cron expression</a>.<br/> * <br/> * This class depends on <a href="https://github.com/jmrozanec/cron-utils">cron-utils</a>, * so this dependency have to be in the classpath in order to be able to use {@link CronSchedule}. * Since cron-utils is marked as optional, it has to be explicitly referenced in the * project dependency configuration (pom.xml, build.gradle, build.sbt etc.). */ public class CronSchedule implements Schedule { private static final CronParser UNIX_CRON_PARSER = new CronParser( CronDefinitionBuilder.instanceDefinitionFor(CronType.UNIX) ); private static final CronParser QUARTZ_CRON_PARSER = new CronParser( CronDefinitionBuilder.instanceDefinitionFor(CronType.QUARTZ) ); private static final CronDescriptor ENGLISH_DESCRIPTOR = CronDescriptor.instance(Locale.ENGLISH); private final ExecutionTime cronExpression; private final String description; private final ZoneId zoneId; public CronSchedule(Cron cronExpression, ZoneId zoneId) { this.cronExpression = ExecutionTime.forCron(cronExpression); this.description = ENGLISH_DESCRIPTOR.describe(cronExpression); this.zoneId = zoneId; } public CronSchedule(Cron cronExpression) { this(cronExpression, ZoneId.systemDefault()); } @Override public long nextExecutionInMillis(long currentTimeInMillis, int executionsCount, Long lastExecutionTimeInMillis) { Instant currentInstant = Instant.ofEpochMilli(currentTimeInMillis); return cronExpression.timeToNextExecution(ZonedDateTime.ofInstant( currentInstant, zoneId )) .map(durationBetweenNextExecution -> currentInstant.plus(durationBetweenNextExecution).toEpochMilli() ) .orElse(Schedule.WILL_NOT_BE_EXECUTED_AGAIN); } @Override public String toString() { return description; } /** * Create a {@link Schedule} from a cron expression based on the Unix format, * e.g. 1 * * * * for each minute. */ public static CronSchedule parseUnixCron(String cronExpression) { return new CronSchedule(UNIX_CRON_PARSER.parse(cronExpression)); } /** * Create a {@link Schedule} from a cron expression based on the Quartz format, * e.g. 0 * * * * ? * for each minute. */ public static CronSchedule parseQuartzCron(String cronExpression) { return new CronSchedule(QUARTZ_CRON_PARSER.parse(cronExpression)); } }