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

com.github.lontime.extquartz.container.SchedulerContainer Maven / Gradle / Ivy

There is a newer version: 1.4.0
Show newest version
package com.github.lontime.extquartz.container;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;

import com.github.lontime.base.commonj.components.ComponentInterfaceHelper;
import com.github.lontime.base.commonj.components.Lifecycle;
import com.github.lontime.base.commonj.utils.CollectionHelper;
import com.github.lontime.base.commonj.utils.LoggerHelper;
import com.github.lontime.extquartz.JobInterface;
import com.github.lontime.extquartz.common.PooledExtConnectionProvider;
import com.github.lontime.extquartz.configuration.InstanceOption;
import com.github.lontime.extquartz.configuration.JobOption;
import com.github.lontime.extquartz.configuration.JobStoreOption;
import com.github.lontime.extquartz.configuration.OptionResolver;
import com.github.lontime.extquartz.configuration.Options;
import com.github.lontime.extquartz.configuration.ThreadPoolOption;
import com.github.lontime.extquartz.configuration.TriggerOption;
import org.quartz.CronScheduleBuilder;
import org.quartz.CronTrigger;
import org.quartz.DailyTimeIntervalScheduleBuilder;
import org.quartz.DailyTimeIntervalTrigger;
import org.quartz.DateBuilder;
import org.quartz.JobBuilder;
import org.quartz.JobDataMap;
import org.quartz.JobDetail;
import org.quartz.JobKey;
import org.quartz.ScheduleBuilder;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SimpleScheduleBuilder;
import org.quartz.SimpleTrigger;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.quartz.TriggerKey;
import org.quartz.impl.DefaultThreadExecutor;
import org.quartz.impl.DirectSchedulerFactory;
import org.quartz.simpl.SimpleThreadPool;
import org.quartz.spi.JobStore;
import org.quartz.spi.ThreadPool;
import org.quartz.utils.DBConnectionManager;

import static org.quartz.CronScheduleBuilder.cronSchedule;
import static org.quartz.DailyTimeIntervalScheduleBuilder.dailyTimeIntervalSchedule;
import static org.quartz.JobBuilder.newJob;
import static org.quartz.SimpleScheduleBuilder.simpleSchedule;
import static org.quartz.TriggerBuilder.newTrigger;

/**
 * SchedulerContainer.
 *
 * @author lontime
 * @since 1.0
 */
public class SchedulerContainer implements Lifecycle {

    private static final DefaultThreadExecutor DEFAULT_THREAD_EXECUTOR = new DefaultThreadExecutor();

    private final Options options;

    private List triggerKeys = new ArrayList<>();

    private Map jobMap = new ConcurrentHashMap<>();

    public SchedulerContainer() {
        this.options = OptionResolver.getInstance().getOptions();
    }

    //    public void createScheduler(String schedulerName,
//                                String schedulerInstanceId, ThreadPool threadPool,
//                                ThreadExecutor threadExecutor,
//                                JobStore jobStore, Map schedulerPluginMap,
//                                String rmiRegistryHost, int rmiRegistryPort,
//                                long idleWaitTime, long dbFailureRetryInterval,
//                                boolean jmxExport, String jmxObjectName, int maxBatchSize, long batchTimeWindow)
    @Override
    public void initialize() {
//        try {
//            final ThreadPool threadPool = buildThreadPool();
//            final JobStore jobStore = createJobStore();
//            final int poolSize = threadPool.getPoolSize();
//            DirectSchedulerFactory.getInstance().createScheduler(
//                    options.getSchedulerName(), options.getSchedulerInstanceId(),
//                    threadPool, DEFAULT_THREAD_EXECUTOR,
//                    jobStore, null,
//                    options.getRmiRegistryHost(), options.getRmiRegistryPort(),
//                    options.getIdleWaitTimeMs(), options.getDbFailureRetryIntervalMs(),
//                    options.getJmxExport(), options.getJmxObjectName(),
//                    (options.getMaxBatchSize() > 0) ? options.getMaxBatchSize()
//                            : poolSize,
//                    options.getBatchTimeWindow().toMillis());
//
//        } catch (SchedulerException e) {
//            LoggerHelper.warnv(e, "initialize error");
//        }
        final ThreadPool threadPool = buildThreadPool();
        initJobStore();
        if (options.getInstances() != null) {
            options.getInstances().forEach(s -> createScheduler(threadPool, s));
        }
    }

    private void createScheduler(ThreadPool threadPool, InstanceOption option) {
        try {
            final JobStore jobStore = createJobStore();
            final int poolSize = threadPool.getPoolSize();
            DirectSchedulerFactory.getInstance().createScheduler(
                    option.getSchedulerName(), option.getSchedulerInstanceId(),
                    threadPool, DEFAULT_THREAD_EXECUTOR,
                    jobStore, null,
                    option.getRmiRegistryHost(), option.getRmiRegistryPort(),
                    option.getIdleWaitTimeMs(), option.getDbFailureRetryIntervalMs(),
                    option.getJmxExport(), option.getJmxObjectName(),
                    (option.getMaxBatchSize() > 0) ? option.getMaxBatchSize() : poolSize,
                    option.getBatchTimeWindow().toMillis());
        } catch (SchedulerException e) {
            LoggerHelper.warnv(e, "initialize error");
        }
    }

    private ThreadPool buildThreadPool() {
        final ThreadPoolOption option = options.getThreadPool();
        final SimpleThreadPool threadPool = new SimpleThreadPool();
        if (option == null) {
            return threadPool;
        }
        if (option.getCount() != null) {
            threadPool.setThreadCount(option.getCount());
        }
        if (option.getPriority() != null) {
            threadPool.setThreadPriority(option.getPriority());
        }
        if (option.getDaemon() != null) {
            threadPool.setMakeThreadsDaemons(option.getDaemon());
        }
        if (option.getInheritGroup() != null) {
            threadPool.setThreadsInheritGroupOfInitializingThread(option.getInheritGroup());
        }
        if (option.getInheritLoader() != null) {
            threadPool.setThreadsInheritContextClassLoaderOfInitializingThread(option.getInheritLoader());
        }
        if (option.getThreadNamePrefix() != null) {
            threadPool.setThreadNamePrefix(option.getThreadNamePrefix());
        }
        return threadPool;
    }

    private void initJobStore() {
        if (options.getJobStore() == null) {
            return;
        }
        final JobStoreOption option = options.getJobStore();
        if (OptionResolver.getInstance().dependOnDatasource()) {
            DBConnectionManager.getInstance().addConnectionProvider(option.getDataSource(),
                    new PooledExtConnectionProvider(option));
        }
    }

    private JobStore createJobStore() {
        final JobStoreOption option = options.getJobStore();
        return option.getKind() == null ? null : option.getKind().toJobStore(option);
    }

    public Scheduler getScheduler(String name) {
        try {
            if (name != null) {
                return DirectSchedulerFactory.getInstance().getScheduler(name);
            }
            return DirectSchedulerFactory.getInstance().getScheduler();
        } catch (SchedulerException e) {
            return null;
        }
    }

    @Override
    public void start() {
        try {
            DirectSchedulerFactory.getInstance().getAllSchedulers().stream().forEach(this::startInternal);
        } catch (Exception ex) {
            LoggerHelper.warnv(ex, "start error");
        }
    }

    private void startInternal(Scheduler scheduler) {
        try {
            final InstanceOption option = OptionResolver.getInstance().getInstanceOption(scheduler.getSchedulerName());
            if (option == null) {
                return;
            }
            if (option.getStartDelayed() != null) {
                scheduler.startDelayed((int) option.getStartDelayed().getSeconds());
            } else {
                scheduler.start();
            }
            triggerJob(scheduler, scheduler.getSchedulerName());
        } catch (Exception ex) {
            LoggerHelper.warnv(ex, "startInternal error");
        }
    }

    private void triggerJob(Scheduler scheduler, String instanceName) {
        options.getTriggers().stream()
                .filter(s -> Objects.nonNull(s.getInstanceName()))
                .filter(s -> s.getInstanceName().equals(instanceName))
                .forEach(s -> {
                    final JobKey jobKey = new JobKey(s.getJobName(), s.getJobGroup());
                    final JobOption jobOption = OptionResolver.getInstance().getJobOption(jobKey);
                    if (jobOption.getDeleted()) {
                        deleteJob(scheduler, jobKey);
                        return;
                    }
                    final List jobs = ComponentInterfaceHelper.get(JobInterface.class, jobKey.toString());
                    if (CollectionHelper.isEmpty(jobs)) {
                        return;
                    }
                    final TriggerKey triggerKey = TriggerKey.triggerKey(s.getName(), s.getGroupName());
                    final Trigger trigger = createTrigger(s, triggerKey);
                    scheduleJobInternal(scheduler, trigger, s);
                });
    }

    private void deleteJob(Scheduler scheduler, JobKey jobKey) {
        try {
            final JobDetail jobDetail = scheduler.getJobDetail(jobKey);
            if (jobDetail != null) {
                scheduler.deleteJob(jobKey);
            }
        } catch (SchedulerException e) {
            LoggerHelper.warnv(e, "deleteJob is error!");
        }
    }

    private JobDetail getJob(JobKey jobKey) {
        return jobMap.computeIfAbsent(jobKey, key -> createJob(key));
    }

    private JobDetail createJob(JobKey jobKey) {
        final JobOption option = OptionResolver.getInstance().getJobOption(jobKey);
        if (option.getDeleted()) {
            return null;
        }
        return createJob(option, jobKey);
    }

    private JobDetail createJob(JobOption jobOption, JobKey jobKey) {
        final List jobs = ComponentInterfaceHelper.get(JobInterface.class, jobKey.toString());
        if (CollectionHelper.isEmpty(jobs)) {
            return null;
        }
        final JobBuilder builder = newJob(jobs.get(0).getClass()).withIdentity(jobKey);
        if (jobOption.getStoreDurably() != null && jobOption.getStoreDurably()) {
            builder.storeDurably();
        }
        if (jobOption.getRequestRecovery() != null) {
            builder.requestRecovery(jobOption.getRequestRecovery());
        }
        if (jobOption.getDataMap() != null) {
            builder.setJobData(new JobDataMap(jobOption.getDataMap()));
        }
        if (jobOption.getDesc() != null) {
            builder.withDescription(jobOption.getDesc());
        }
        return builder.build();
    }

    private Trigger createTrigger(TriggerOption triggerOption, TriggerKey triggerKey) {
        final TriggerBuilder triggerBuilder = newTrigger().withIdentity(triggerKey);
        if (triggerOption.getStartFuture() != null) {
            triggerBuilder.startAt(DateBuilder.futureDate((int) triggerOption.getStartFuture().toMillis(),
                    DateBuilder.IntervalUnit.MILLISECOND));
        } else {
            triggerBuilder.startNow();
        }
        if (triggerOption.getExpire() != null) {
            triggerBuilder.endAt(DateBuilder.futureDate((int) triggerOption.getExpire().toMillis(),
                    DateBuilder.IntervalUnit.MILLISECOND));
        }
        final TriggerOption.Scheduler schedulerOption = triggerOption.getScheduler();
        if (schedulerOption.getCron() != null) {
            triggerBuilder.withSchedule(createCronSchedule(triggerOption.getScheduler().getCron(),
                    schedulerOption.getMisfireInstruction()));
        } else if (schedulerOption.getSimple() != null) {
            triggerBuilder.withSchedule(createSimpleSchedule(triggerOption.getScheduler().getSimple(),
                    schedulerOption.getMisfireInstruction()));
        } else if (schedulerOption.getDailyTimeInterval() != null) {
            triggerBuilder.withSchedule(createDailyTimeInterval(triggerOption.getScheduler().getDailyTimeInterval(),
                    schedulerOption.getMisfireInstruction()));
        }
        triggerBuilder.forJob(JobKey.jobKey(triggerOption.getJobName(), triggerOption.getJobGroup()));
        if (triggerOption.getJobData() != null) {
            triggerBuilder.usingJobData(new JobDataMap(triggerOption.getJobData()));
        }
        return triggerBuilder.build();
    }

    private ScheduleBuilder createDailyTimeInterval(TriggerOption.DailyTimeInterval option, Integer misfireInstruction) {
        final DailyTimeIntervalScheduleBuilder builder = dailyTimeIntervalSchedule();
        if (misfireInstruction != null) {
            if (misfireInstruction == Trigger.MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY) {
                builder.withMisfireHandlingInstructionIgnoreMisfires();
            }
            if (misfireInstruction == DailyTimeIntervalTrigger.MISFIRE_INSTRUCTION_DO_NOTHING) {
                builder.withMisfireHandlingInstructionDoNothing();
            }
            if (misfireInstruction == DailyTimeIntervalTrigger.MISFIRE_INSTRUCTION_FIRE_ONCE_NOW) {
                builder.withMisfireHandlingInstructionFireAndProceed();
            }
        }
        if (option.getInterval() != null) {
            builder.withInterval((int) option.getInterval().toMillis(), DateBuilder.IntervalUnit.MILLISECOND);
        }
        if (option.getDaysOfWeek() != null) {
            builder.onDaysOfTheWeek(option.getDaysOfWeek());
        }
        if (option.getStartTimeOfDay() != null) {
            builder.startingDailyAt(option.getStartTimeOfDay());
        }
        if (option.getEndTimeOfDay() != null) {
            builder.endingDailyAt(option.getEndTimeOfDay());
        }
        if (option.getEndingDailyAfterCount() != null) {
            builder.endingDailyAfterCount(option.getEndingDailyAfterCount());
        }
        if (option.getEveryDay() != null && option.getEveryDay()) {
            builder.onEveryDay();
        }
        if (option.getMondayThroughFriday() != null && option.getMondayThroughFriday()) {
            builder.onMondayThroughFriday();
        }
        if (option.getSaturdayAndSunday() != null && option.getSaturdayAndSunday()) {
            builder.onSaturdayAndSunday();
        }
        if (option.getRepeatCount() != null) {
            builder.withRepeatCount(option.getRepeatCount());
        }
        return builder;
    }

    private ScheduleBuilder createCronSchedule(TriggerOption.Cron option, Integer misfireInstruction) {
        final CronScheduleBuilder builder = cronSchedule(option.getExpression());
        if (misfireInstruction != null) {
            if (misfireInstruction == Trigger.MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY) {
                builder.withMisfireHandlingInstructionIgnoreMisfires();
            }
            if (misfireInstruction == CronTrigger.MISFIRE_INSTRUCTION_DO_NOTHING) {
                builder.withMisfireHandlingInstructionDoNothing();
            }
            if (misfireInstruction == CronTrigger.MISFIRE_INSTRUCTION_FIRE_ONCE_NOW) {
                builder.withMisfireHandlingInstructionFireAndProceed();
            }
            if (option.getTimeZone() != null) {
                builder.inTimeZone(option.getTimeZone());
            }
        }
        return builder;
    }

    private ScheduleBuilder createSimpleSchedule(TriggerOption.Simple option, Integer misfireInstruction) {
        final SimpleScheduleBuilder builder = simpleSchedule();
        if (option.getInterval() != null) {
            builder.withIntervalInMilliseconds(option.getInterval().toMillis());
        }
        if (option.getRepeatCount() != null) {
            builder.withRepeatCount(option.getRepeatCount());
        }
        if (misfireInstruction != null) {
            if (misfireInstruction == Trigger.MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY) {
                builder.withMisfireHandlingInstructionIgnoreMisfires();
            }
            if (misfireInstruction == SimpleTrigger.MISFIRE_INSTRUCTION_FIRE_NOW) {
                builder.withMisfireHandlingInstructionFireNow();
            }
            if (misfireInstruction == SimpleTrigger.MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_EXISTING_COUNT) {
                builder.withMisfireHandlingInstructionNextWithExistingCount();
            }
            if (misfireInstruction == SimpleTrigger.MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT) {
                builder.withMisfireHandlingInstructionNextWithRemainingCount();
            }
            if (misfireInstruction == SimpleTrigger.MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_EXISTING_REPEAT_COUNT) {
                builder.withMisfireHandlingInstructionNowWithExistingCount();
            }
            if (misfireInstruction == SimpleTrigger.MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_REMAINING_REPEAT_COUNT) {
                builder.withMisfireHandlingInstructionNowWithRemainingCount();
            }
        }
        return builder;
    }

    private void scheduleJobInternal(Scheduler scheduler, Trigger trigger, TriggerOption option) {
        try {
            triggerKeys.add(trigger.getKey());
            if (scheduler.checkExists(trigger.getKey())) {
                scheduler.rescheduleJob(trigger.getKey(), trigger);
                return;
            }
            final JobDetail job = getJob(trigger.getJobKey());
            if (job == null) {
                LoggerHelper.warnv("not found job for {0}", trigger.getKey().toString());
                return;
            }
            scheduler.addJob(job, option.getReplaceJob(), option.getStoreNonDurableWhileAwaitingScheduling());
            scheduler.scheduleJob(trigger);
        } catch (Exception ex) {
            LoggerHelper.warnv(ex, "scheduleJob error");
        }
    }

    @Override
    public void stop() {
        try {
            final Collection allSchedulers = DirectSchedulerFactory.getInstance()
                    .getAllSchedulers();
            if (CollectionHelper.isNotEmpty(allSchedulers)) {
                new ArrayList<>(allSchedulers).stream().forEach(s -> stopInternal(s));
            }
        } catch (Exception ex) {
            LoggerHelper.warnv(ex, "stop error");
        }
    }

    private void stopInternal(Scheduler scheduler) {
        try {
            final InstanceOption option = OptionResolver.getInstance()
                    .getInstanceOption(scheduler.getSchedulerName());
            if (option == null) {
                return;
            }
            LoggerHelper.infov("stopInternal is now, {0}", scheduler.isShutdown());
            if (scheduler.isShutdown()) {
                return;
            }
            if (option.getWaitForJobsToComplete() != null) {
                scheduler.shutdown(option.getWaitForJobsToComplete());
            } else {
                scheduler.shutdown();
            }
        } catch (Exception ex) {
            LoggerHelper.warnv(ex, "startInternal error");
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy