package com.app.mvc.schedule;
import com.app.mvc.acl.convert.BaseConvert;
import com.app.mvc.beans.JsonMapper;
import com.app.mvc.beans.PageQuery;
import com.app.mvc.beans.PageResult;
import com.app.mvc.util.DateTimeUtil;
import com.google.common.base.Function;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.quartz.CronScheduleBuilder;
import org.quartz.CronTrigger;
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SchedulerFactory;
import org.quartz.TriggerBuilder;
import org.quartz.TriggerKey;
import org.quartz.TriggerUtils;
import org.quartz.impl.StdSchedulerFactory;
import org.quartz.impl.triggers.CronTriggerImpl;
import org.springframework.stereotype.Service;
import javax.annotation.Nullable;
import javax.annotation.Resource;
import java.text.ParseException;
import java.util.Date;
import java.util.List;
/**
* Created by jimin on 16/5/8.
*/
@Slf4j
@Service
public class ScheduleService {
@Resource
private ScheduleJobSettingDao scheduleJobSettingDao;
@Resource
private ScheduleExecuteResultDao scheduleExecuteResultDao;
private static Scheduler scheduler = null;
static {
if (scheduler == null) {
init();
}
}
public synchronized static void init() {
if (scheduler != null) {
return;
}
SchedulerFactory factory = new StdSchedulerFactory();
try {
scheduler = factory.getScheduler();
scheduler.start();
} catch (Throwable t) {
log.error("create schedule exception", t);
}
}
/**
* 把所有可能的job放入调度中, 项目启动时使用
*/
public void scheduleAll() {
if (scheduler == null) {
init();
}
List<ScheduledJobSetting> list = scheduleJobSettingDao.getAll();
for (ScheduledJobSetting setting : list) {
try {
schedule(setting);
} catch (Throwable t) {
log.error("schedule current job exception, {}", JsonMapper.obj2String(setting));
}
}
}
/**
* 调度指定的job(调整job)
*/
private void schedule(ScheduledJobSetting setting) throws SchedulerException {
// triggerId 使用 scheduledId 代替
String triggerId = setting.getScheduleId();
String groupId = setting.getGroupId();
String jobId = setting.getScheduleId();
if (!scheduler.isStarted()) {
throw new SchedulerException("scheduler is not started");
}
TriggerKey triggerKey = TriggerKey.triggerKey(triggerId, groupId);
if (scheduler.checkExists(triggerKey)) {
// 如果已经在调度
if (StringUtils.isBlank(setting.getCron()) || setting.getStatus() != ScheduleJobStatus.STARTED.getCode()) {
// 需要移除调度
removeSchedule(setting);
} else {
// 需要重新调度
CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(triggerKey).withSchedule(CronScheduleBuilder.cronSchedule(setting.getCron()))
.build();
scheduler.rescheduleJob(TriggerKey.triggerKey(triggerId, groupId), trigger);
}
} else if (StringUtils.isNotBlank(setting.getCron()) && setting.getStatus() == ScheduleJobStatus.STARTED.getCode()) {
// 如果不再调度, 看看是否需要调度
Class clazz;
try {
clazz = Class.forName(setting.getClassPath());
} catch (ClassNotFoundException e) {
log.error("not found class, cannot schedule, className:{}", setting.getClassPath());
throw new SchedulerException("not found class, cannot schedule");
}
JobDetail job = JobBuilder.newJob(clazz).withIdentity(jobId, groupId).build();
CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(triggerKey).withSchedule(CronScheduleBuilder.cronSchedule(setting.getCron()))
.build();
scheduler.scheduleJob(job, trigger);
}
}
/**
* 停止调度某个job
*/
private void removeSchedule(ScheduledJobSetting setting) throws SchedulerException {
// triggerId 使用 scheduledId 代替
String triggerId = setting.getScheduleId();
String groupId = setting.getGroupId();
if (!scheduler.isStarted()) {
throw new SchedulerException("scheduler is not started");
}
scheduler.unscheduleJob(TriggerKey.triggerKey(triggerId, groupId));
}
/**
* 更新某个job
*/
public void updateJobSetting(ScheduledJobSetting setting) throws SchedulerException {
scheduleJobSettingDao.update(setting);
schedule(setting);
}
public ScheduledJobSetting findJobSetting(int id) {
return scheduleJobSettingDao.findById(id);
}
public List<ScheduleJobDto> getAll() {
List<ScheduledJobSetting> settingList = scheduleJobSettingDao.getAll();
if (CollectionUtils.isEmpty(settingList)) {
return Lists.newArrayList();
}
List<ScheduleJobDto> scheduleJobDtoList = Lists.transform(settingList, new Function<ScheduledJobSetting, ScheduleJobDto>() {
@Nullable
@Override
public ScheduleJobDto apply(@Nullable ScheduledJobSetting setting) {
String resultScheduleId = setting.getGroupId() + "_" + setting.getScheduleId();
ScheduleExecuteResult lastResult = scheduleExecuteResultDao.findLastExecute(resultScheduleId);
ScheduleJobDto dto = ScheduleJobDto.builder().id(setting.getId()).groupId(setting.getGroupId()).scheduleId(setting.getScheduleId())
.status(setting.getStatus()).cron(setting.getCron()).build();
if (lastResult != null) {
dto.setLastExecuteTime(DateTimeUtil.dateTimeFrom(lastResult.getStartTime()));
if (lastResult.getEndTime() != null) {
dto.setCostMillSeconds(lastResult.getEndTime().getTime() - lastResult.getStartTime().getTime());
} else {
dto.setCostMillSeconds(0);
}
}
if (setting.getStatus() == ScheduleJobStatus.STARTED.getCode() && StringUtils.isNotBlank(setting.getCron())) {
CronTriggerImpl cronTriggerImpl = new CronTriggerImpl();
try {
cronTriggerImpl.setCronExpression(setting.getCron());
} catch (ParseException e) {
}
List<Date> dates = TriggerUtils.computeFireTimes(cronTriggerImpl, null, 1);
if (CollectionUtils.isNotEmpty(dates)) {
dto.setNextExecuteTime(DateTimeUtil.dateTimeFrom(dates.get(0)));
}
}
return dto;
}
});
return scheduleJobDtoList;
}
public List<ScheduledJobSetting> getListByGroupId(String groupId) {
return scheduleJobSettingDao.getListByGroupId(groupId);
}
public PageResult<ScheduleExecuteResultDto> getPageByScheduleId(String scheduleId, PageQuery page) {
BaseConvert.checkPara(page);
int count = scheduleExecuteResultDao.countByScheduleId(scheduleId);
if (count > 0) {
List<ScheduleExecuteResult> list = scheduleExecuteResultDao.getPageByScheduleId(scheduleId, page);
return PageResult.<ScheduleExecuteResultDto>builder().total(count)
.data(Lists.transform(list, new Function<ScheduleExecuteResult, ScheduleExecuteResultDto>() {
@Nullable
@Override
public ScheduleExecuteResultDto apply(@Nullable ScheduleExecuteResult result) {
if (result.getEndTime() != null) {
return ScheduleExecuteResultDto.builder().scheduleId(result.getScheduleId())
.start(DateTimeUtil.dateTimeFrom(result.getStartTime())).end(DateTimeUtil.dateTimeFrom(result.getEndTime()))
.status(result.getStatus()).costMillSeconds(result.getEndTime().getTime() - result.getStartTime().getTime()).build();
} else {
return ScheduleExecuteResultDto.builder().scheduleId(result.getScheduleId())
.start(DateTimeUtil.dateTimeFrom(result.getStartTime())).status(result.getStatus()).costMillSeconds(0).build();
}
}
})).build();
} else {
return PageResult.<ScheduleExecuteResultDto>builder().build();
}
}
}