jfinal定时调度任务插件QuartzPlugin

jfinal与quartz结合。

即可以在配置文件中配置定时任务。又可动态添加新的定时任务。

使用中的项目:

http://www.3dmomoda.com

依赖jar包:quartz-2.0.2.jar

其他的依赖自己稍微改下就可以


quartz_config.properties(quartz配置)

#============================================================================
# 配置主要调度程序属性
#============================================================================
org.quartz.scheduler.instanceName = DefaultQuartzScheduler
org.quartz.scheduler.instanceId = AUTO
org.quartz.scheduler.rmi.export = false
org.quartz.scheduler.rmi.proxy = false
org.quartz.scheduler.wrapJobExecutionInUserTransaction = false

#============================================================================
# 配置线程池  
#============================================================================
org.quartz.threadPool.threadCount = 10
org.quartz.threadPool.threadPriority = 5

#============================================================================
# 配置任务 
#============================================================================
org.quartz.jobStore.misfireThreshold = 60000

org.quartz.scheduler.skipUpdateCheck = true

quartz_job.properties(任务配置,默认的调度任务可以在这里添加)

#开启的任务列表“,”隔开
jobArray = task1,task2

#搭客任务定时结束
task1.job=com.momoda.quartz.Task1
task1.cron=00 00 23 * * ?
task1.desc=任务1

#归档登录日志
task2.job=com.momoda.quartz.Task2
task2.cron=00 00 04 * * ?
task2.desc=任务2

FormaterCronExpression.java(日期转cron定时表达式类)

public class FormaterCronExpression {
	static String formart = "yyyy-MM-dd HH:mm:ss";

	public static String formaterCronExpression(String date) {
		SimpleDateFormat format = new SimpleDateFormat(formart.substring(0, date.length() - 1));
		SimpleDateFormat format2 = new SimpleDateFormat("yyyy MM dd HH mm ss");
		try {
			Date d = format.parse(date);
			date = format2.format(d);
			String[] dateArry = date.split(" ");
			String exp = dateArry[5] + " " + dateArry[4] + " " + dateArry[3] + " " + dateArry[2] + " " + dateArry[1]
					+ " ? " + dateArry[0];
			return exp;
		} catch (ParseException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return "";
	}

}

JobBean.java(任务对象实体bean)

/**
 * 计划任务信息
 */
public class JobBean {

	/** 任务id */
	private String jobId;

	/** 任务描述 */
	private String jobDesc;

	/** 任务运行时间表达式 */
	private String cronExpression;

	/** 任务分组 */
	private String jobGroup;

	/** 任务类 */
	private String jobClass;

	public String getJobId() {
		return jobId;
	}

	public void setJobId(String jobId) {
		this.jobId = jobId;
	}

	public String getJobDesc() {
		return jobDesc;
	}

	public void setJobDesc(String jobDesc) {
		this.jobDesc = jobDesc;
	}

	public String getCronExpression() {
		return cronExpression;
	}

	public void setCronExpression(String cronExpression) {
		this.cronExpression = cronExpression;
	}

	public String getJobGroup() {
		return jobGroup;
	}

	public void setJobGroup(String jobGroup) {
		this.jobGroup = jobGroup;
	}

	public String getJobClass() {
		return jobClass;
	}

	public void setJobClass(String jobClass) {
		this.jobClass = jobClass;
	}

	public JobBean(String jobId, String jobDesc, String cronExpression, String jobGroup, String jobClass) {
		this.jobId = jobId;
		this.jobDesc = jobDesc;
		this.cronExpression = cronExpression;
		this.jobGroup = jobGroup;
		this.jobClass = jobClass;
	}

	public JobBean() {
		super();
	}
}

QuartzPlugin.java(插件类)

public class QuartzPlugin implements IPlugin {
	private List<JobBean> jobs = new ArrayList<JobBean>();
	private SchedulerFactory sf;
	private static Scheduler scheduler;
	private String jobConfig;
	private String confConfig;
	private Map<String, String> jobProp;

	public QuartzPlugin(String jobConfig, String confConfig) {
		this.jobConfig = jobConfig;
		this.confConfig = confConfig;
	}

	public QuartzPlugin(String jobConfig) {
		this.jobConfig = jobConfig;
		this.confConfig = "/quartz_config.properties";
	}

	public QuartzPlugin() {
		this.jobConfig = "/quartz_job.properties";
		this.confConfig = "/quartz_config.properties";
	}

	public static void addJob(JobBean job) {
		try {
			TriggerKey triggerKey = TriggerKey.triggerKey(job.getJobDesc(), job.getJobGroup());
			CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
			// 不存在,创建一个
			if (null == trigger) {
				Class<Job> j2 = (Class<Job>) Class.forName(job.getJobClass());
				JobDetail jobDetail = JobBuilder.newJob(j2).withIdentity(job.getJobDesc(), job.getJobGroup()).build();
				jobDetail.getJobDataMap().put("scheduleJob", job);

				// 表达式调度构建器
				CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(job.getCronExpression());

				// 按新的cronExpression表达式构建一个新的trigger
				trigger = TriggerBuilder.newTrigger().withIdentity(job.getJobDesc(), job.getJobGroup())
						.withSchedule(scheduleBuilder).build();
				try {
					scheduler.scheduleJob(jobDetail, trigger);
				} catch (Exception e) {
					e.printStackTrace();
				}
			} else {
				// Trigger已存在,那么更新相应的定时设置
				// 表达式调度构建器
				CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(job.getCronExpression());

				// 按新的cronExpression表达式重新构建trigger
				trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();

				// 按新的trigger重新设置job执行
				scheduler.rescheduleJob(triggerKey, trigger);
			}
		} catch (Exception e) {
		}
	}

	@Override
	public boolean start() {
		loadJobsFromProperties();
		startJobs();
		return true;
	}

	private void startJobs() {
		try {
			if (StrKit.notBlank(confConfig)) {
				sf = new StdSchedulerFactory(confConfig);
			} else {
				sf = new StdSchedulerFactory();
			}
			scheduler = sf.getScheduler();
		} catch (SchedulerException e) {
			Throwables.propagate(e);
		}
		for (JobBean entry : jobs) {
			addJob(entry);
		}
		try {
			scheduler.start();
		} catch (SchedulerException e) {
			Throwables.propagate(e);
		}
	}

	private void loadJobsFromProperties() {
		if (StrKit.isBlank(jobConfig)) {
			return;
		}
		jobProp = ResourceKit.readProperties(jobConfig);
		String jobArray = jobProp.get("jobArray");
		if (StrKit.isBlank(jobArray)) {
			return;
		}
		String[] jobArrayList = jobArray.split(",");
		for (String jobName : jobArrayList) {
			jobs.add(createJobBean(jobName));
		}
	}

	private JobBean createJobBean(String key) {
		JobBean job = new JobBean();
		job.setJobClass(jobProp.get(key + ".job"));
		job.setCronExpression(jobProp.get(key + ".cron"));
		job.setJobGroup(jobProp.get(key));
		job.setJobDesc(jobProp.get(key + ".desc"));
		return job;
	}

	@Override
	public boolean stop() {
		try {
			scheduler.shutdown();
		} catch (SchedulerException e) {
			Throwables.propagate(e);
		}
		return true;
	}
}


configPlugin方法中将插件启动就可以


动态添加定时任务

JobBean job = new JobBean();
job.setJobClass("com.momoda.quartz.DakeTaskJob");
job.setCronExpression(FormaterCronExpression.formaterCronExpression(task.getStr("closingTime")));
job.setJobGroup("DakeTaskJob");
job.setJobDesc("DakeTaskJob_" + taskid);
QuartzPlugin.addJob(job);


评论区

独自等待遗梦

2017-05-17 18:36

为何添加动态任务,报空指针?
JobBean job = new JobBean();
job.setJobClass("com.cmb.urumqi.tips.test.HelloJobTh");
job.setCronExpression("15 0,5,10,15,20,25,30,35,40,45,50,55 * * * ?");
//job.setCronExpression(FormaterCronExpression.formaterCronExpression(task.getStr("closingTime")));
job.setJobGroup("DakeTaskJob");
//job.setJobDesc("DakeTaskJob_" + taskid);
job.setJobDesc("DakeTaskJob_333");
System.out.println("----job--" + job);
QuartzPlugin.addJob(job);
因为不知道 作者 task 和 taskID获取方式,所以自己改了一下。。但是执行报错
java.lang.NullPointerException
at com.cmb.urumqi.tips.quartz.QuartzPlugin.addJob(QuartzPlugin.java:59)
CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
这一句,求指教,非常感谢。

埋头苦干

2017-05-23 14:03

@独自等待遗梦

看下加载插件的方式对不对。

public boolean start() {
loadJobsFromProperties();
startJobs();
return true;
}

尤其是startJobs() 方式是否执行了。

另外该插件适配于jfinal2.2,更高版本的未经测试。

独自等待遗梦

2017-05-26 12:26

@埋头苦干 用的是Jfinal3.0,静态的启动没有问题。。。主要就是动态增加任务。。DEBUG是因为 CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey); 执行的时候scheduler为空

524233006

2017-08-08 21:19

@eagle ResourceKit在哪个包,能否告知

Muart

2017-08-14 10:01

@524233006 https://code.google.com/archive/p/jfinal-ext/downloads jfinal-ext-2.1.jar

Muart

2017-08-14 10:02

@eagle ResourceKit 在 https://code.google.com/archive/p/jfinal-ext/downloads jfinal-ext-2.1.jar

fmpoffice

2017-09-02 15:45

很好,感谢楼主

wwe52010

2017-09-22 11:40

请问ResourceKit.readProperties这个加载properties文件是怎么使用的。我项目中没有这个包

wwe52010

2017-09-22 11:41

谢谢,就在楼上。已找到

pfjia

2017-10-10 16:21

你好,请问这个插件的动态添加任务功能应该是必须在QuartzPlugin.start()之前吧,因为scheduler.start();是在startJob()方法中才被调用,如果 调用addJob()后没有执行quartzPlugin.start(),动态添加的任务应该还是没有被执行吧 @埋头苦干

坐拥花丛

2017-11-17 12:23

很好!感谢楼主分享!

埋头苦干

2017-12-27 16:07

@pfjia 启动之后也可以添加。要不然怎么叫动态添加嘛。

埋头苦干

2017-12-27 16:10

@独自等待遗梦 QuartzPlugin 类中 定义了 静态的 private static Scheduler scheduler;后面的startJobs()中给它赋值了。

pfjia

2018-01-18 15:30

@埋头苦干 明白了,多谢楼主

夏天的绅士

2018-03-31 11:25

task.getStr这个是什么?可以告诉下吗

mrbai2018

2018-04-11 15:17

这个动态创建的job 如果服务器重新启动了,是否会丢失?还是写到什么配置文件里面了?请指教

埋头苦干

2018-07-12 17:50

@mrbai2018 自己存到数据库里就行啦

埋头苦干

2018-07-12 17:51

@夏天的绅士 会用jfinal...这个问题应该不会问出来的

chh123456

2018-08-23 18:00

请问:按照上面的代码写完之后会报错的原因

chh123456

2018-08-23 18:01

Illegal access: this web application instance has been stopped already. Could not load net.sf.ehcache.store.disk.DiskStore$KeySet. The eventual following stack trace is caused by an error thrown for debugging purposes as well as to attempt to terminate the thread which caused the illegal access, and has no functional impact.
java.lang.IllegalStateException
at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1813)
at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1771)
at net.sf.ehcache.store.disk.DiskStore.keySet(DiskStore.java:610)
at net.sf.ehcache.store.disk.DiskStorageFactory$DiskExpiryTask.run(DiskStorageFactory.java:817)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
at java.util.concurrent.FutureTask.runAndReset(FutureTask.java:308)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$301(ScheduledThreadPoolExecutor.java:180)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:294)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)

年轻似水

2018-09-03 18:44

@埋头苦干 动态添加定时任务里面的代码 在哪个位置写?

小马奔腾

2019-01-29 14:03

找你这样使用的,出现:java.lang.IllegalArgumentException: Job class must implement the Job interface.

KingTiger

2019-05-07 10:05

@JFinal https://www.jfinal.com/doc/9-3 按照这个方法使用的,但是只调用了task1的任务,task2的并没有被调用,这是为啥呢?

jianghe727

2019-06-01 14:10

有没有人在jfinal中用过监听器?JobListener和TriggerListener?

JFinal

2019-06-01 15:28

@jianghe727 在 jfinal 中使用监听器与往常没有什么不同,直接在 web.xml 中添加即可

如果用的是 jfinal undertow, 添加监听器参考文档:
https://www.jfinal.com/doc/1-4

注意看第 12 小节:添加 Filter、WebSocket、Servlet、Listener