随着需求的增加,现在要定时启动一个调度和计划任务,原先写的QuartzPlugin,是持久化保存到数据库中的,从数据库中读取任务并执行。要是添加一个每天循环任务,就要在代码里写一次开始任务的代码,执行后,再注释掉,最后重启项目。否则会因为启动同name,同group的任务而报错
1org.quartz.ObjectAlreadyExistsException: Unable to store Job : 2 'group_test_1.job_test_1', because one already exists with this identification.
原先JFinal-ext中是通过配置文件,定时循环运行的,两者结合不就是我要的吗?
思路清晰,持久化依旧,再在QuartzPlugin 中增加读取配置文件后生成调度任务,当然,这个也会保存到数据库中的,所以还要在停止的时候,终止和删除相应调度任务,否则下次启动就会报冲突了。
修改的QuartzPlugin 依旧参考了jfinal-ext,做了一些修改,原先会依赖com.google.guava:guava这个jar,因为本人有点强迫症,不喜欢jar包太多,所以能不加就不加了。
1import com.jfinal.plugin.IPlugin; 2import org.quartz.*; 3import org.quartz.impl.StdSchedulerFactory; 4import sedion.net.gppz.quartz.QuartzFactory; 5import sedion.net.gppz.utils.PropertiesUtil; 6 7import java.util.*; 8 9import static org.quartz.JobBuilder.newJob; 10import static org.quartz.TriggerBuilder.newTrigger; 11 12/** 13 * Quartz插件 14 * 15 * @author WWF 16 */ 17public class QuartzPlugin implements IPlugin { 18 19 private static final String JOB = "job"; 20 21 /** 22 * 默认配置文件 23 **/ 24 private String config = "quartz.properties"; 25 private String job_config = "jobs.properties"; 26 private Map<Job, String> jobs = new LinkedHashMap<>(); 27 private Map<String, String> jobProp; 28 29 30 public QuartzPlugin() { 31 32 } 33 34 public QuartzPlugin(String config) { 35 this.config = config; 36 } 37 38 public QuartzPlugin(String config, String job_config) { 39 this.config = config; 40 this.job_config = job_config; 41 } 42 43 @Override 44 public boolean start() { 45 try { 46 //加载配置文件 47 Properties props = PropertiesUtil.loadPropertyFile(config); 48 //实例化 49 QuartzFactory.sf = new StdSchedulerFactory(props); 50 //获取Scheduler 51 Scheduler sched = QuartzFactory.sf.getScheduler(); 52 53 //从配置文件中加载定时任务 54 loadJobsFromProperties(); 55 if (jobs.size() > 0) { 56 Set<Map.Entry<Job, String>> set = jobs.entrySet(); 57 for (Map.Entry<Job, String> entry : set) { 58 Job job = entry.getKey(); 59 String jobClassName = job.getClass().getName(); 60 String jobCronExp = entry.getValue(); 61 JobDetail jobDetail = newJob(job.getClass()) 62 .withIdentity(jobClassName, jobClassName) 63 .requestRecovery() 64 .build(); 65 CronTrigger trigger = newTrigger() 66 .withIdentity(jobClassName, jobClassName) 67 .withSchedule(CronScheduleBuilder.cronSchedule(jobCronExp)) 68 .build(); 69 sched.scheduleJob(jobDetail, trigger); 70 } 71 } 72 sched.start(); 73 return true; 74 } catch (Exception e) { 75 e.printStackTrace(); 76 } 77 return false; 78 } 79 80 @Override 81 public boolean stop() { 82 try { 83 //从配置文件中加载定时任务 84 loadJobsFromProperties(); 85 Scheduler sched = QuartzFactory.sf.getScheduler(); 86 if (jobs.size() > 0) { 87 Set<Map.Entry<Job, String>> set = jobs.entrySet(); 88 for (Map.Entry<Job, String> entry : set) { 89 Job job = entry.getKey(); 90 String jobClassName = job.getClass().getName(); 91 TriggerKey triggerKey = TriggerKey.triggerKey(jobClassName, jobClassName); 92 Trigger trigger = sched.getTrigger(triggerKey); 93 if (trigger != null) { 94 sched.pauseTrigger(triggerKey);// 停止触发器 95 sched.unscheduleJob(triggerKey);// 移除触发器 96 sched.deleteJob(trigger.getJobKey());// 删除任务 97 } 98 } 99 } 100 sched.shutdown(); 101 QuartzFactory.sf = null; 102 return true; 103 } catch (Exception e) { 104 e.printStackTrace(); 105 } 106 return false; 107 } 108 109 /** 110 * 从配置文件中加载定时任务 111 */ 112 private void loadJobsFromProperties() { 113 //清空任务 114 jobs.clear(); 115 Properties properties = PropertiesUtil.loadPropertyFile(job_config); 116 jobProp = new HashMap<>((Map) properties); 117 Set<Map.Entry<String, String>> entries = jobProp.entrySet(); 118 for (Map.Entry<String, String> entry : entries) { 119 String key = entry.getKey(); 120 if (!key.endsWith(JOB) || !isEnableJob(key)) { 121 continue; 122 } 123 String jobClassName = jobProp.get(key) + ""; 124 String jobCronExp = jobProp.get(cronKey(key)) + ""; 125 try { 126 Class c = Class.forName(jobClassName); 127 jobs.put((Job) c.newInstance(), jobCronExp); 128 } catch (Exception e) { 129 e.printStackTrace(); 130 } 131 } 132 } 133 134 /** 135 * 获得cron表达式 136 * @param key 137 * @return 138 */ 139 private String cronKey(String key) { 140 return key.substring(0, key.lastIndexOf(".") + 1) + "cron"; 141 } 142 143 /** 144 * 是否开启 145 * @param key 146 * @return 147 */ 148 private boolean isEnableJob(String key) { 149 String enableKey = key.substring(0, key.indexOf(".") + 1) + "enable"; 150 Object enable = jobProp.get(enableKey); 151 if (enable != null && "true".equalsIgnoreCase((enable + "").trim())) { 152 return true; 153 } 154 return false; 155 } 156 157}
在看jfinal-ext源码的时候,有个疑问,
看介绍,job的配置文件是
1#JobA 2a.job=test.com.jfinal.plugin.quzrtz.JobA 3a.cron=*/5 * * * * ? 4a.enable=true
但是在代码中,转化为map后,获取相应的key的处理是
1private String enable(String key) { 2 return key.substring(0, key.lastIndexOf(JOB)) + "enable"; 3} 4 5private String cronKey(String key) { 6 return key.substring(0, key.lastIndexOf(JOB)) + "cron"; 7}
所以我觉得配置文件应该是
1#JobA 2a.job=test.com.jfinal.plugin.quzrtz.JobA 3a.job.cron=*/5 * * * * ? 4a.job.enable=true
或者是获得key的截取改一下
再次感谢jfinal 和jfinal-ext。