content: package com.xxl.job.admin.core.schedule;
  
  import java.util.Date;
  import java.util.HashSet;
  import java.util.concurrent.ConcurrentHashMap;
  
  import javax.annotation.PostConstruct;
  import javax.annotation.PreDestroy;
  import javax.annotation.Resource;
  
  import org.quartz.CronScheduleBuilder;
  import org.quartz.CronTrigger;
  import org.quartz.Job;
  import org.quartz.JobBuilder;
  import org.quartz.JobDetail;
  import org.quartz.JobKey;
  import org.quartz.Scheduler;
  import org.quartz.SchedulerException;
  import org.quartz.Trigger;
  import org.quartz.Trigger.TriggerState;
  import org.quartz.TriggerBuilder;
  import org.quartz.TriggerKey;
  import org.quartz.impl.triggers.CronTriggerImpl;
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  import org.springframework.beans.BeansException;
  import org.springframework.context.ApplicationContext;
  import org.springframework.context.ApplicationContextAware;
  import org.springframework.stereotype.Component;
  import org.springframework.util.Assert;
  
  import com.xxl.job.admin.core.jobbean.RemoteHttpJobBean;
  import com.xxl.job.admin.core.model.XxlJobInfo;
  import com.xxl.job.admin.core.thread.JobFailMonitorHelper;
  import com.xxl.job.admin.core.thread.JobRegistryMonitorHelper;
  import com.xxl.job.admin.core.util.I18nUtil;
  import com.xxl.job.admin.dao.XxlJobGroupDao;
  import com.xxl.job.admin.dao.XxlJobInfoDao;
  import com.xxl.job.admin.dao.XxlJobLogDao;
  import com.xxl.job.admin.dao.XxlJobRegistryDao;
  import com.xxl.job.core.biz.AdminBiz;
  import com.xxl.job.core.biz.ExecutorBiz;
  import com.xxl.job.core.enums.ExecutorBlockStrategyEnum;
  import com.xxl.job.core.rpc.netcom.NetComClientProxy;
  import com.xxl.job.core.rpc.netcom.NetComServerFactory;
  
  /**
   * base quartz scheduler util
   * @author xuxueli 2015-12-19 16:13:53
   */
  @Component
  public final class XxlJobDynamicScheduler implements ApplicationContextAware {
      private static final Logger logger = LoggerFactory.getLogger(XxlJobDynamicScheduler.class);
  
      // ---------------------- param ----------------------
  
      // scheduler
      private static Scheduler scheduler;
      public void setScheduler(Scheduler scheduler) {
  		XxlJobDynamicScheduler.scheduler = scheduler;
  	}
  
  	// accessToken
      private static String accessToken;
      public void setAccessToken(String accessToken) {
          this.accessToken = accessToken;
      }
  
      // dao
      public static XxlJobLogDao xxlJobLogDao;
      public static XxlJobInfoDao xxlJobInfoDao;
      public static XxlJobRegistryDao xxlJobRegistryDao;
      public static XxlJobGroupDao xxlJobGroupDao;
      public static AdminBiz adminBiz;
  
      // ---------------------- applicationContext ----------------------
      @Override
  	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
      	XxlJobDynamicScheduler.scheduler = applicationContext.getBean("scheduler",Scheduler.class);
  		XxlJobDynamicScheduler.xxlJobLogDao = applicationContext.getBean(XxlJobLogDao.class);
  		XxlJobDynamicScheduler.xxlJobInfoDao = applicationContext.getBean(XxlJobInfoDao.class);
          XxlJobDynamicScheduler.xxlJobRegistryDao = applicationContext.getBean(XxlJobRegistryDao.class);
          XxlJobDynamicScheduler.xxlJobGroupDao = applicationContext.getBean(XxlJobGroupDao.class);
          XxlJobDynamicScheduler.adminBiz = applicationContext.getBean(AdminBiz.class);
  	}
  
      // ---------------------- init + destroy ----------------------
      @PostConstruct
      public void init() throws Exception {
          // admin registry monitor run
          JobRegistryMonitorHelper.getInstance().start();
  
          // admin monitor run
          JobFailMonitorHelper.getInstance().start();
  
          // admin-server(spring-mvc)
          NetComServerFactory.putService(AdminBiz.class, XxlJobDynamicScheduler.adminBiz);
          NetComServerFactory.setAccessToken(accessToken);
  
          // init i18n
          initI18n();
  
          // valid
          Assert.notNull(scheduler, "quartz scheduler is null");
          logger.info(">>>>>>>>> init xxl-job admin success.");
      }
  
      private void initI18n(){
          for (ExecutorBlockStrategyEnum item:ExecutorBlockStrategyEnum.values()) {
              item.setTitle(I18nUtil.getString("jobconf_block_".concat(item.name())));
          }
      }
      @PreDestroy
      public void destroy(){
          // admin registry stop
          JobRegistryMonitorHelper.getInstance().toStop();
  
          // admin monitor stop
          JobFailMonitorHelper.getInstance().toStop();
      }
  
      // ---------------------- executor-client ----------------------
      private static ConcurrentHashMap<String, ExecutorBiz> executorBizRepository = new ConcurrentHashMap<String, ExecutorBiz>();
      public static ExecutorBiz getExecutorBiz(String address) throws Exception {
          // valid
          if (address==null || address.trim().length()==0) {
              return null;
          }
  
          // load-cache
          address = address.trim();
          ExecutorBiz executorBiz = executorBizRepository.get(address);
          if (executorBiz != null) {
              return executorBiz;
          }
  
          // set-cache
          executorBiz = (ExecutorBiz) new NetComClientProxy(ExecutorBiz.class, address, accessToken).getObject();
          executorBizRepository.put(address, executorBiz);
          return executorBiz;
      }
  
      // ---------------------- schedule util ----------------------
  
      /**
       * fill job info
       *
       * @param jobInfo
       */
  	public static void fillJobInfo(XxlJobInfo jobInfo) {
  		// TriggerKey : name + group
          String group = String.valueOf(jobInfo.getJobGroup());
          String name = String.valueOf(jobInfo.getId());
          TriggerKey triggerKey = TriggerKey.triggerKey(name, group);
  
          try {
  			Trigger trigger = scheduler.getTrigger(triggerKey);
  
  			TriggerState triggerState = scheduler.getTriggerState(triggerKey);
  			
  			// parse params
  			if (trigger!=null && trigger instanceof CronTriggerImpl) {
  				String cronExpression = ((CronTriggerImpl) trigger).getCronExpression();
  				jobInfo.setJobCron(cronExpression);
  			}
  
  			//JobKey jobKey = new JobKey(jobInfo.getJobName(), String.valueOf(jobInfo.getJobGroup()));
              //JobDetail jobDetail = scheduler.getJobDetail(jobKey);
              //String jobClass = jobDetail.getJobClass().getName();
  
  			if (triggerState!=null) {
  				jobInfo.setJobStatus(triggerState.name());
  			}
  			
  		} catch (SchedulerException e) {
  			logger.error(e.getMessage(), e);
  		}
  	}
  	
      /**
       * check if exists
       *
       * @param jobName
       * @param jobGroup
       * @return
       * @throws SchedulerException
       */
  	public static boolean checkExists(String jobName, String jobGroup) throws SchedulerException{
  		TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroup);
  		return scheduler.checkExists(triggerKey);
  	}
  
      /**
       * addJob
       *
       * @param jobName
       * @param jobGroup
       * @param cronExpression
       * @return
       * @throws SchedulerException
       */
  	public static boolean addJob(String jobName, String jobGroup, String cronExpression) throws SchedulerException {
      	// TriggerKey : name + group
          TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroup);
          JobKey jobKey = new JobKey(jobName, jobGroup);
          
          // TriggerKey valid if_exists
          if (checkExists(jobName, jobGroup)) {
              logger.info(">>>>>>>>> addJob fail, job already exist, jobGroup:{}, jobName:{}", jobGroup, jobName);
              return false;
          }
          
          // CronTrigger : TriggerKey + cronExpression	// withMisfireHandlingInstructionDoNothing 忽略掉调度终止过程中忽略的调度
          CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(cronExpression).withMisfireHandlingInstructionDoNothing();
          CronTrigger cronTrigger = TriggerBuilder.newTrigger().withIdentity(triggerKey).withSchedule(cronScheduleBuilder).build();
  
          // JobDetail : jobClass
  		Class<? extends Job> jobClass_ = RemoteHttpJobBean.class;   // Class.forName(jobInfo.getJobClass());
          
  		JobDetail jobDetail = JobBuilder.newJob(jobClass_).withIdentity(jobKey).build();
          /*if (jobInfo.getJobData()!=null) {
          	JobDataMap jobDataMap = jobDetail.getJobDataMap();
          	jobDataMap.putAll(JacksonUtil.readValue(jobInfo.getJobData(), Map.class));	
          	// JobExecutionContext context.getMergedJobDataMap().get("mailGuid");
  		}*/
          
          // schedule : jobDetail + cronTrigger
          Date date = scheduler.scheduleJob(jobDetail, cronTrigger);
  
          logger.info(">>>>>>>>>>> addJob success, jobDetail:{}, cronTrigger:{}, date:{}", jobDetail, cronTrigger, date);
          return true;
      }
      
      /**
       * rescheduleJob
       *
       * @param jobGroup
       * @param jobName
       * @param cronExpression
       * @return
       * @throws SchedulerException
       */
  	public static boolean rescheduleJob(String jobGroup, String jobName, String cronExpression) throws SchedulerException {
      	
      	// TriggerKey valid if_exists
          if (!checkExists(jobName, jobGroup)) {
          	logger.info(">>>>>>>>>>> rescheduleJob fail, job not exists, JobGroup:{}, JobName:{}", jobGroup, jobName);
              return false;
          }
          
          // TriggerKey : name + group
          TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroup);
          CronTrigger oldTrigger = (CronTrigger) scheduler.getTrigger(triggerKey);
  
          if (oldTrigger != null) {
              // avoid repeat
              String oldCron = oldTrigger.getCronExpression();
              if (oldCron.equals(cronExpression)){
                  return true;
              }
  
              // CronTrigger : TriggerKey + cronExpression
              CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(cronExpression).withMisfireHandlingInstructionDoNothing();
              oldTrigger = oldTrigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(cronScheduleBuilder).build();
  
              // rescheduleJob
              scheduler.rescheduleJob(triggerKey, oldTrigger);
          } else {
              // CronTrigger : TriggerKey + cronExpression
              CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(cronExpression).withMisfireHandlingInstructionDoNothing();
              CronTrigger cronTrigger = TriggerBuilder.newTrigger().withIdentity(triggerKey).withSchedule(cronScheduleBuilder).build();
  
              // JobDetail-JobDataMap fresh
              JobKey jobKey = new JobKey(jobName, jobGroup);
              JobDetail jobDetail = scheduler.getJobDetail(jobKey);
              /*JobDataMap jobDataMap = jobDetail.getJobDataMap();
              jobDataMap.clear();
              jobDataMap.putAll(JacksonUtil.readValue(jobInfo.getJobData(), Map.class));*/
  
              // Trigger fresh
              HashSet<Trigger> triggerSet = new HashSet<Trigger>();
              triggerSet.add(cronTrigger);
  
              scheduler.scheduleJob(jobDetail, triggerSet, true);
          }
  
          logger.info(">>>>>>>>>>> resumeJob success, JobGroup:{}, JobName:{}", jobGroup, jobName);
          return true;
      }
      
      /**
       * unscheduleJob
       *
       * @param jobName
       * @param jobGroup
       * @return
       * @throws SchedulerException
       */
      public static boolean removeJob(String jobName, String jobGroup) throws SchedulerException {
      	// TriggerKey : name + group
          TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroup);
          boolean result = false;
          if (checkExists(jobName, jobGroup)) {
              result = scheduler.unscheduleJob(triggerKey);
              logger.info(">>>>>>>>>>> removeJob, triggerKey:{}, result [{}]", triggerKey, result);
          }
          return true;
      }
  
      /**
       * pause
       *
       * @param jobName
       * @param jobGroup
       * @return
       * @throws SchedulerException
       */
      public static boolean pauseJob(String jobName, String jobGroup) throws SchedulerException {
      	// TriggerKey : name + group
      	TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroup);
          
          boolean result = false;
          if (checkExists(jobName, jobGroup)) {
              scheduler.pauseTrigger(triggerKey);
              result = true;
              logger.info(">>>>>>>>>>> pauseJob success, triggerKey:{}", triggerKey);
          } else {
          	logger.info(">>>>>>>>>>> pauseJob fail, triggerKey:{}", triggerKey);
          }
          return result;
      }
      
      /**
       * resume
       *
       * @param jobName
       * @param jobGroup
       * @return
       * @throws SchedulerException
       */
      public static boolean resumeJob(String jobName, String jobGroup) throws SchedulerException {
      	// TriggerKey : name + group
      	TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroup);
          
          boolean result = false;
          if (checkExists(jobName, jobGroup)) {
              scheduler.resumeTrigger(triggerKey);
              result = true;
              logger.info(">>>>>>>>>>> resumeJob success, triggerKey:{}", triggerKey);
          } else {
          	logger.info(">>>>>>>>>>> resumeJob fail, triggerKey:{}", triggerKey);
          }
          return result;
      }
      
      /**
       * run
       *
       * @param jobName
       * @param jobGroup
       * @return
       * @throws SchedulerException
       */
      public static boolean triggerJob(String jobName, String jobGroup) throws SchedulerException {
      	// TriggerKey : name + group
      	JobKey jobKey = new JobKey(jobName, jobGroup);
          
          boolean result = false;
          if (checkExists(jobName, jobGroup)) {
              scheduler.triggerJob(jobKey);
              result = true;
              logger.info(">>>>>>>>>>> runJob success, jobKey:{}", jobKey);
          } else {
          	logger.info(">>>>>>>>>>> runJob fail, jobKey:{}", jobKey);
          }
          return result;
      }
  
      /**
       * finaAllJobList
       *
       * @return
       *//*
      @Deprecated
      public static List<Map<String, Object>> finaAllJobList(){
          List<Map<String, Object>> jobList = new ArrayList<Map<String,Object>>();
  
          try {
              if (scheduler.getJobGroupNames()==null || scheduler.getJobGroupNames().size()==0) {
                  return null;
              }
              String groupName = scheduler.getJobGroupNames().get(0);
              Set<JobKey> jobKeys = scheduler.getJobKeys(GroupMatcher.jobGroupEquals(groupName));
              if (jobKeys!=null && jobKeys.size()>0) {
                  for (JobKey jobKey : jobKeys) {
                      TriggerKey triggerKey = TriggerKey.triggerKey(jobKey.getName(), Scheduler.DEFAULT_GROUP);
                      Trigger trigger = scheduler.getTrigger(triggerKey);
                      JobDetail jobDetail = scheduler.getJobDetail(jobKey);
                      TriggerState triggerState = scheduler.getTriggerState(triggerKey);
                      Map<String, Object> jobMap = new HashMap<String, Object>();
                      jobMap.put("TriggerKey", triggerKey);
                      jobMap.put("Trigger", trigger);
                      jobMap.put("JobDetail", jobDetail);
                      jobMap.put("TriggerState", triggerState);
                      jobList.add(jobMap);
                  }
              }
  
          } catch (SchedulerException e) {
              logger.error(e.getMessage(), e);
              return null;
          }
          return jobList;
      }*/
  
  }