All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.kagkarlsson.scheduler.task.schedule.CronSchedule Maven / Gradle / Ivy

/**
 * Copyright (C) Gustav Karlsson
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.github.kagkarlsson.scheduler.task.schedule;

import com.github.kagkarlsson.shaded.cronutils.model.Cron;
import com.github.kagkarlsson.shaded.cronutils.model.CronType;
import com.github.kagkarlsson.shaded.cronutils.model.definition.CronDefinitionBuilder;
import com.github.kagkarlsson.shaded.cronutils.model.time.ExecutionTime;
import com.github.kagkarlsson.shaded.cronutils.parser.CronParser;
import com.github.kagkarlsson.scheduler.task.ExecutionComplete;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.Serializable;
import java.time.Duration;
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.temporal.ChronoUnit;
import java.util.Optional;

/**
 * Spring-style cron-pattern schedule
 */
public class CronSchedule implements Schedule, Serializable {

    private static final String DISABLED = "-";
    private static final Logger LOG = LoggerFactory.getLogger(CronSchedule.class);
    private final String pattern;
    private final ZoneId zoneId;
    private transient ExecutionTime cronExecutionTime; // lazily initialized

    private CronSchedule() { // For serializers
        pattern = null;
        zoneId = ZoneId.systemDefault();
    }
    public CronSchedule(String pattern) {
        this(pattern, ZoneId.systemDefault());
    }

    public CronSchedule(String pattern, ZoneId zoneId) {
        this.pattern = pattern;
        if (zoneId == null) {
            throw new IllegalArgumentException("zoneId may not be null");
        }
        this.zoneId = zoneId;
        lazyInitExecutionTime();
    }

    @Override
    public Instant getNextExecutionTime(ExecutionComplete executionComplete) {
        lazyInitExecutionTime(); // for deserialized objects
        ZonedDateTime lastDone = ZonedDateTime.ofInstant(executionComplete.getTimeDone(), zoneId);  //frame the 'last done' time in the context of the time zone for this schedule
        //so that expressions like "0 05 13,20 * * ?" (New York) can operate in the
        // context of the desired time zone

        Optional nextTime = cronExecutionTime.nextExecution(lastDone);
        if (!nextTime.isPresent()) {
            LOG.error("Cron-pattern did not return any further execution-times. This behavior is currently not supported by the scheduler. Setting next execution-time to far-future.");
            return Instant.now().plus(1000, ChronoUnit.YEARS);
        }
        return nextTime.get().toInstant();
    }

    private void lazyInitExecutionTime() {
        if (cronExecutionTime != null) {
            return;
        }

        synchronized (this) {
            if (cronExecutionTime == null) {
                if (isDisabled()) {
                    cronExecutionTime = new CronSchedule.DisabledScheduleExecutionTime();
                } else {
                    CronParser parser = new CronParser(CronDefinitionBuilder.instanceDefinitionFor(CronType.SPRING));
                    Cron cron = parser.parse(pattern);
                    cronExecutionTime = ExecutionTime.forCron(cron);
                }
            }
        }
    }

    @Override
    public boolean isDeterministic() {
        return true;
    }

    @Override
    public String toString() {
        return "CronSchedule pattern=" + pattern + ", zone=" + zoneId;
    }

    public String getPattern() {
        return pattern;
    }

    public ZoneId getZoneId() {
        return zoneId;
    }

    private static class DisabledScheduleExecutionTime implements ExecutionTime {
        @Override
        public Optional nextExecution(ZonedDateTime date) {
            throw unsupportedException();
        }

        @Override
        public Optional timeToNextExecution(ZonedDateTime date) {
            throw unsupportedException();
        }

        @Override
        public Optional lastExecution(ZonedDateTime date) {
            throw unsupportedException();
        }

        @Override
        public Optional timeFromLastExecution(ZonedDateTime date) {
            throw unsupportedException();
        }

        @Override
        public boolean isMatch(ZonedDateTime date) {
            throw unsupportedException();
        }

        private UnsupportedOperationException unsupportedException() {
            return new UnsupportedOperationException("Schedule is marked as disabled. Method should never be called");
        }
    }

    @Override
    public boolean isDisabled() {
        return DISABLED.equals(pattern);
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy