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

com.turbospaces.quartz.JobsDispatcher Maven / Gradle / Ivy

package com.turbospaces.quartz;

import java.util.Date;
import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
import java.util.function.Supplier;

import org.apache.commons.lang3.time.StopWatch;
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.JobExecutionContext;
import org.quartz.JobPersistenceException;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SimpleScheduleBuilder;
import org.quartz.Trigger;
import org.quartz.Trigger.CompletedExecutionInstruction;
import org.quartz.Trigger.TriggerState;
import org.quartz.TriggerBuilder;
import org.quartz.TriggerKey;
import org.quartz.impl.triggers.CronTriggerImpl;
import org.quartz.impl.triggers.SimpleTriggerImpl;
import org.quartz.listeners.TriggerListenerSupport;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.base.Suppliers;
import com.turbospaces.common.PlatformUtil;

import reactor.core.publisher.Mono;

public interface JobsDispatcher {
    default StopWatch runOnceBlock(Scheduler scheduler, JobBuilder job, TriggerKey triggerKey) {
        Mono flux = runOnce(scheduler, job, triggerKey);
        return flux.block();
    }
    default Mono runOnce(Scheduler scheduler, JobBuilder job, TriggerKey triggerKey) {
        JobDetail jobDetail = job.storeDurably().build();
        return Mono.fromCallable(new Callable() {
            @Override
            public StopWatch call() throws Exception {
                StopWatch stopWatch = StopWatch.createStarted();

                String k = "run-once-uuid";
                String v = PlatformUtil.randomUUID().toString();

                TriggerBuilder now = TriggerBuilder.newTrigger();
                now.withIdentity(triggerKey);
                now.startAt(new Date()); // ~ as of now
                now.forJob(jobDetail);
                now.usingJobData(k, v);

                try {
                    CountDownLatch latch = new CountDownLatch(1);
                    TriggerListenerSupport listener = new TriggerListenerSupport() {
                        @Override
                        public String getName() {
                            return now.toString();
                        }
                        @Override
                        public void triggerComplete(Trigger trigger, JobExecutionContext context, CompletedExecutionInstruction triggerInstructionCode) {
                            if (context.getJobDetail().equals(jobDetail)) {
                                if (v.equals(context.getTrigger().getJobDataMap().get(k))) {
                                    latch.countDown();
                                }
                            }
                        }
                    };

                    scheduler.getListenerManager().addTriggerListener(listener); // ~ add listener
                    scheduler.addJob(jobDetail, true);
                    scheduler.scheduleJob(now.build());
                    latch.await(); // ~ await for completion
                    scheduler.getListenerManager().removeTriggerListener(listener.getName()); // ~ remove listener

                    return stopWatch;
                } catch (Exception err) {
                    throw new RuntimeException(err);
                }
            }
        });
    }
    default void scheduleEnabledJob(Scheduler instance, JobBuilder job, Trigger trigger, Supplier runOnStart) throws SchedulerException {
        scheduleJob(instance, job, trigger, Suppliers.ofInstance(true), runOnStart);
    }
    default void scheduleJob(
            Scheduler instance,
            JobBuilder job,
            Trigger trigger,
            Supplier enabled,
            Supplier runOnStart) throws SchedulerException {
        JobDetail jobDetail = job.storeDurably().build();

        //
        // if there is not such class anymore
        //
        try {
            instance.getJobDetail(jobDetail.getKey());
        } catch (JobPersistenceException err) {
            if (err.getCause() instanceof ClassNotFoundException) {
                instance.deleteJob(jobDetail.getKey());
            }
        }

        if (enabled.get()) {
            if (runOnStart.get()) {
                if (instance.checkExists(jobDetail.getKey())) {
                    instance.triggerJob(jobDetail.getKey());
                }
            }
        }

        // replace trigger if needed
        Logger logger = LoggerFactory.getLogger(getClass());
        if (instance.checkExists(jobDetail.getKey())) {
            if (enabled.get()) {
                boolean reschedule = false;
                Trigger prev = instance.getTrigger(trigger.getKey());
                if (prev != null) {
                    if (prev.getClass().equals(trigger.getClass())) {
                        if (trigger instanceof SimpleTriggerImpl newTrigger) {
                            SimpleTriggerImpl prevTrigger = (SimpleTriggerImpl) prev;
                            if (newTrigger.getRepeatInterval() == prevTrigger.getRepeatInterval()) {
                                logger.debug("simple trigger = {} has not changed since last run ...", trigger.getKey());
                            } else {
                                reschedule = true;
                            }
                        } else if (trigger instanceof CronTriggerImpl newTrigger) {
                            CronTriggerImpl prevTrigger = (CronTriggerImpl) prev;
                            if (newTrigger.getCronExpression().equals(prevTrigger.getCronExpression())) {
                                logger.debug("cron trigger = {} has not changed since last run ...", trigger.getKey());
                            } else {
                                reschedule = true;
                            }
                        }
                    }
                }

                instance.resumeJob(jobDetail.getKey());

                if (reschedule) {
                    logger.info("detected changes in trigger, re-scheduling job={} to use new trigger={} now ...", jobDetail.getKey(), trigger);
                    instance.rescheduleJob(trigger.getKey(), trigger);
                } else {
                    TriggerState state = instance.getTriggerState(trigger.getKey());
                    logger.debug("trigger {} is currently is in state: {}", trigger.getKey(), state.name().toLowerCase());
                }
            } else {
                logger.info("pausing existing job = {}", jobDetail.getKey());
                instance.pauseJob(jobDetail.getKey());
            }
        } else {
            if (enabled.get()) {
                logger.info("scheduling new job = {} now", jobDetail.getKey());
                instance.scheduleJob(jobDetail, trigger);
            }
        }
    }
    default Trigger secondlyTriggerWithMisfireNowWithExistingCount(TriggerKey triggerKey, Supplier frequency) {
        SimpleScheduleBuilder schedule = SimpleScheduleBuilder.repeatSecondlyForever(frequency.get()).withMisfireHandlingInstructionNowWithExistingCount();
        return TriggerBuilder.newTrigger().withIdentity(triggerKey).withSchedule(schedule).build();
    }
    default Trigger secondlyTriggerWithMisfireNowWithRemainingCount(TriggerKey triggerKey, Supplier frequency) {
        SimpleScheduleBuilder schedule = SimpleScheduleBuilder.repeatSecondlyForever(frequency.get()).withMisfireHandlingInstructionNowWithRemainingCount();
        return TriggerBuilder.newTrigger().withIdentity(triggerKey).withSchedule(schedule).build();
    }
    default Trigger secondlyTriggerWithMisfireNextWithExistingCount(TriggerKey triggerKey, Supplier frequency) {
        SimpleScheduleBuilder schedule = SimpleScheduleBuilder.repeatSecondlyForever(frequency.get()).withMisfireHandlingInstructionNextWithExistingCount();
        return TriggerBuilder.newTrigger().withIdentity(triggerKey).withSchedule(schedule).build();
    }
    default Trigger secondlyTriggerWithMisfireNextWithRemainingCount(TriggerKey triggerKey, Supplier frequency) {
        SimpleScheduleBuilder schedule = SimpleScheduleBuilder.repeatSecondlyForever(frequency.get()).withMisfireHandlingInstructionNextWithRemainingCount();
        return TriggerBuilder.newTrigger().withIdentity(triggerKey).withSchedule(schedule).build();
    }
    default Trigger minutelyTriggerWithMisfireNowWithExistingCount(TriggerKey triggerKey, Supplier frequency) {
        SimpleScheduleBuilder schedule = SimpleScheduleBuilder.repeatMinutelyForever(frequency.get()).withMisfireHandlingInstructionNowWithExistingCount();
        return TriggerBuilder.newTrigger().withIdentity(triggerKey).withSchedule(schedule).build();
    }
    default Trigger minutelyTriggerWithMisfireNowWithRemainingCount(TriggerKey triggerKey, Supplier frequency) {
        SimpleScheduleBuilder schedule = SimpleScheduleBuilder.repeatMinutelyForever(frequency.get()).withMisfireHandlingInstructionNowWithRemainingCount();
        return TriggerBuilder.newTrigger().withIdentity(triggerKey).withSchedule(schedule).build();
    }
    default Trigger minutelyTriggerWithMisfireNextWithExistingCount(TriggerKey triggerKey, Supplier frequency) {
        SimpleScheduleBuilder schedule = SimpleScheduleBuilder.repeatMinutelyForever(frequency.get()).withMisfireHandlingInstructionNextWithExistingCount();
        return TriggerBuilder.newTrigger().withIdentity(triggerKey).withSchedule(schedule).build();
    }
    default Trigger minutelyTriggerWithMisfireNextWithRemainingCount(TriggerKey triggerKey, Supplier frequency) {
        SimpleScheduleBuilder schedule = SimpleScheduleBuilder.repeatMinutelyForever(frequency.get()).withMisfireHandlingInstructionNextWithRemainingCount();
        return TriggerBuilder.newTrigger().withIdentity(triggerKey).withSchedule(schedule).build();
    }
    default Trigger hourlyWithMisfireNowWithExistingCount(TriggerKey triggerKey, Supplier frequency) {
        SimpleScheduleBuilder schedule = SimpleScheduleBuilder.repeatHourlyForever(frequency.get()).withMisfireHandlingInstructionNowWithExistingCount();
        return TriggerBuilder.newTrigger().withIdentity(triggerKey).withSchedule(schedule).build();
    }
    default Trigger hourlyTriggerWithMisfireNowWithRemainingCount(TriggerKey triggerKey, Supplier frequency) {
        SimpleScheduleBuilder schedule = SimpleScheduleBuilder.repeatHourlyForever(frequency.get()).withMisfireHandlingInstructionNowWithRemainingCount();
        return TriggerBuilder.newTrigger().withIdentity(triggerKey).withSchedule(schedule).build();
    }
    default Trigger hourlyTriggerWithMisfireNextWithExistingCount(TriggerKey triggerKey, Supplier frequency) {
        SimpleScheduleBuilder schedule = SimpleScheduleBuilder.repeatHourlyForever(frequency.get()).withMisfireHandlingInstructionNextWithExistingCount();
        return TriggerBuilder.newTrigger().withIdentity(triggerKey).withSchedule(schedule).build();
    }
    default Trigger hourlyTriggerWithMisfireNextWithRemainingCount(TriggerKey triggerKey, Supplier frequency) {
        SimpleScheduleBuilder schedule = SimpleScheduleBuilder.repeatHourlyForever(frequency.get()).withMisfireHandlingInstructionNextWithRemainingCount();
        return TriggerBuilder.newTrigger().withIdentity(triggerKey).withSchedule(schedule).build();
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy