dropwizard-jobs/dropwizard-jobs

View on GitHub
dropwizard-jobs-core/src/main/java/io/dropwizard/jobs/scheduler/EveryScheduler.java

Summary

Maintainability
A
0 mins
Test Coverage
package io.dropwizard.jobs.scheduler;

import io.dropwizard.jobs.Job;
import io.dropwizard.jobs.JobConfiguration;
import io.dropwizard.jobs.JobMediator;
import io.dropwizard.jobs.ScheduledJob;
import io.dropwizard.jobs.annotations.DelayStart;
import io.dropwizard.jobs.annotations.Every;
import io.dropwizard.jobs.parser.TimeParserUtil;
import org.quartz.JobKey;
import org.quartz.SimpleScheduleBuilder;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;

import java.time.Instant;
import java.util.Date;
import java.util.stream.Stream;

import static io.dropwizard.jobs.scheduler.AnnotationReader.readDurationFromConfig;
import static org.apache.commons.lang3.StringUtils.isEmpty;

public class EveryScheduler extends JobScheduler {
    public EveryScheduler(JobMediator mediator) {
        super(mediator);
    }

    @Override
    public void schedule() {
        this.scheduledJobs()
                .forEach(mediator::scheduleOrRescheduleJob);
    }

    protected Stream<ScheduledJob> scheduledJobs() {
        return mediator.getJobs()
                .allEvery()
                .map(job -> {
                    Class<? extends Job> clazz = job.getClass();
                    Every everyAnnotation = clazz.getAnnotation(Every.class);

                    long interval = getInterval(everyAnnotation, clazz);
                    SimpleScheduleBuilder scheduleBuilder = SimpleScheduleBuilder.simpleSchedule()
                            .withIntervalInMilliseconds(interval);

                    int repeatCount = everyAnnotation.repeatCount();
                    applyRepeatCount(repeatCount, scheduleBuilder);

                    Every.MisfirePolicy misfirePolicy = everyAnnotation.misfirePolicy();
                    applyMisfirePolicy(misfirePolicy, scheduleBuilder);

                    Instant start = extractStart(clazz);
                    int priority = everyAnnotation.priority();
                    Trigger trigger = TriggerBuilder.newTrigger().withSchedule(scheduleBuilder)
                            .startAt(Date.from(start))
                            .withPriority(priority)
                            .build();

                    // ensure that only one instance of each job is scheduled
                    JobKey jobKey = createJobKey(everyAnnotation.jobName(), job);
                    String message = extractMessage(clazz, jobKey);
                    boolean requestRecovery = everyAnnotation.requestRecovery();
                    boolean storeDurably = everyAnnotation.storeDurably();
                    return new ScheduledJob(jobKey, clazz, trigger, requestRecovery, storeDurably, message);
                });
    }

    private long getInterval(Every everyAnnotation, Class<? extends Job> clazz) {
        String value = everyAnnotation.value();
        String expression = durationOrPlainExpression(value, clazz);
        return TimeParserUtil.parseDuration(expression);
    }

    protected String durationOrPlainExpression(String expression, Class<? extends Job> clazz) {
        if (expression.isEmpty() || expression.matches("\\$\\{.*\\}")) {
            JobConfiguration configuration = mediator.getConfiguration();
            String fromConfig = readDurationFromConfig(expression, clazz, configuration);
            expression = !isEmpty(fromConfig) ? fromConfig : expression;
            log.info(clazz + " is configured in the config file to run every " + expression);
        }
        return expression;
    }

    private void applyRepeatCount(int repeatCount, SimpleScheduleBuilder scheduleBuilder) {
        if (repeatCount > -1)
            scheduleBuilder.withRepeatCount(repeatCount);
        else
            scheduleBuilder.repeatForever();
    }

    private void applyMisfirePolicy(Every.MisfirePolicy misfirePolicy, SimpleScheduleBuilder scheduleBuilder) {
        switch (misfirePolicy) {
            case IGNORE_MISFIRES:
                scheduleBuilder.withMisfireHandlingInstructionIgnoreMisfires();
                break;
            case FIRE_NOW:
                scheduleBuilder.withMisfireHandlingInstructionFireNow();
                break;
            case NOW_WITH_EXISTING_COUNT:
                scheduleBuilder.withMisfireHandlingInstructionNowWithExistingCount();
                break;
            case NOW_WITH_REMAINING_COUNT:
                scheduleBuilder.withMisfireHandlingInstructionNowWithRemainingCount();
                break;
            case NEXT_WITH_EXISTING_COUNT:
                scheduleBuilder.withMisfireHandlingInstructionNextWithExistingCount();
                break;
            case NEXT_WITH_REMAINING_COUNT:
                scheduleBuilder.withMisfireHandlingInstructionNextWithRemainingCount();
                break;
            default:
                log.warn("Nothing to do for the misfire policy: {}", misfirePolicy);
                break;
        }
    }

    private String extractMessage(Class<? extends Job> clazz, JobKey jobKey) {
        DelayStart delayAnnotation = clazz.getAnnotation(DelayStart.class);
        Every everyAnnotation = clazz.getAnnotation(Every.class);
        StringBuilder sb = new StringBuilder();
        sb.append(String.format("    %-7s %s", everyAnnotation.value(), jobKey.toString()));
        if (delayAnnotation != null) {
            sb.append(" (").append(delayAnnotation.value()).append(" delay)");
        }
        return sb.toString();
    }

    private Instant extractStart(Class<? extends Job> clazz) {
        Instant start = Instant.now();
        DelayStart delayAnnotation = clazz.getAnnotation(DelayStart.class);
        if (delayAnnotation != null) {
            long milliSecondDelay = TimeParserUtil.parseDuration(delayAnnotation.value());
            start = start.plusMillis(milliSecondDelay);
        }
        return start;
    }

}