com.github.lontime.extquartz.container.SchedulerContainer Maven / Gradle / Ivy
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