Quartz源码——JobStore保存JonDetail和Trigger源码分析(一)
我都是分析的jobStore 方式为jdbc的SimpleTrigger!RAM的方式类似分析方式!
{0} :表的前缀 ,如表qrtz_trigger ,{0}== qrtz_
{1}:quartz.properties 中配置的 org.quartz.scheduler.instanceName: myInstanceName ,{1} ==myInstanceName
要使用定时器,并讲任务持久到数据库,我们一定明白JobDetail和Trigger是如何操作进入数据库,如何注册到Scheduler中!前面我分析了Scheduler的start()和QuartzSchedulerThread的run()!这些都是要在下面讲的这个流程的基础上!
那么下面就开始分析源码,分析源码前首先加一个小的demo,方面解释!
//1.创建Scheduler的工厂
SchedulerFactory sf = new StdSchedulerFactory();
//2.从工厂中获取调度器实例
Scheduler scheduler = sf.getScheduler();
//3.创建JobDetail
JobDetail jb = JobBuilder.newJob(RAMJob.class)
.withDescription("this is a ram job") //job的描述
.withIdentity("ramJob", "ramGroup") //job 的name和group
.build();
//任务运行的时间,SimpleSchedle类型触发器有效
long time= System.currentTimeMillis() + 3*1000L; //3秒后启动任务
Date statTime = new Date(time);
//4.创建Trigger
//使用SimpleScheduleBuilder或者CronScheduleBuilder
Trigger t = TriggerBuilder.newTrigger()
.withDescription("")
.withIdentity("ramTrigger", "ramTriggerGroup")
//.withSchedule(SimpleScheduleBuilder.simpleSchedule())
.startAt(statTime) //默认当前时间启动
.withSchedule(CronScheduleBuilder.cronSchedule("0/2 * * * * ?")) //两秒执行一次
.build();
//5.注册任务和定时器
scheduler.scheduleJob(jb, t);//源码分析
//6.启动 调度器
scheduler.start();
真正的源码分析开始了!!!!
主要分析下面这段,从这里进去,看看源码里面到底是怎么进行处理的!
scheduler.scheduleJob(jb, t);//源码分析
/**
* <p>
* Calls the equivalent method on the 'proxied' <code>QuartzScheduler</code>.
* 调用“代理”<QuartzScheduler>上的等效方法。
*
* 实现 在 StdScheduler.scheduleJob(JobDetail jobDetail, Trigger trigger)
* </p>
*/
public Date scheduleJob(JobDetail jobDetail, Trigger trigger)
throws SchedulerException {
//这里的sched 是 QuartzScheduler 对象,Quartz和核心类,Quartz调度器
return sched.scheduleJob(jobDetail, trigger);//保存JobDetail和Trigger
}
ched.scheduleJob(jobDetail, trigger);//保存JobDetail和Trigger
/**
* <p>
* Add the <code>{@link org.quartz.Job}</code> identified by the given
* <code>{@link org.quartz.JobDetail}</code> to the Scheduler, and
* associate the given <code>{@link org.quartz.Trigger}</code> with it.
* </p>
*
* <p>
* If the given Trigger does not reference any <code>Job</code>, then it
* will be set to reference the Job passed with it into this method.
* </p>
*
* @throws SchedulerException
* if the Job or Trigger cannot be added to the Scheduler, or
* there is an internal Scheduler error.
* 将给定org.quartz.JobDetail标识的org.quartz.Job添加到Scheduler,
* 并将给定的org.quartz.Trigger与其关联。
* 如果给定的触发器不引用任何作业,则它将被设置为引用与其一起传递的作业到此方法中。
*
* 实现在 QuartzScheduler.scheduleJob(JobDetail jobDetail,
* Trigger trigger)
*/
public Date scheduleJob(JobDetail jobDetail,
Trigger trigger) throws SchedulerException {
validateState();//验证调度器是否关闭,关闭抛出异常
//检查 jobDetail和trigger
if (jobDetail == null) {
throw new SchedulerException("JobDetail cannot be null");
}
if (trigger == null) {
throw new SchedulerException("Trigger cannot be null");
}
if (jobDetail.getKey() == null) {
throw new SchedulerException("Job's key cannot be null");
}
if (jobDetail.getJobClass() == null) {
throw new SchedulerException("Job's class cannot be null");
}
OperableTrigger trig = (OperableTrigger)trigger;
//getJobKey 获取 getJobName(), getJobGroup()
if (trigger.getJobKey() == null) {
trig.setJobKey(jobDetail.getKey());
} else if (!trigger.getJobKey().equals(jobDetail.getKey())) {
throw new SchedulerException(
"Trigger does not reference given job!");
}
//验证trigger
trig.validate();
Calendar cal = null;
if (trigger.getCalendarName() != null) {
cal = resources.getJobStore().retrieveCalendar(trigger.getCalendarName());//检索Calendar
"SELECT * FROM {0}CALENDARS WHERE SCHED_NAME = {1} AND CALENDAR_NAME = ?"
}
//在触发器首次添加到调度程序时由调度程序调用,以便让触发器基于任何关联的日历计算
//其第一次触发时间。调用此方法后,getNextFireTime()应返回有效的答案。
Date ft = trig.computeFirstFireTime(cal);
if (ft == null) {
throw new SchedulerException(
"Based on configured schedule, the given trigger '" + trigger.getKey() + "' will never fire.");
}
//存储给定的org.quartz.JobDetail和org.quartz.Trigger。
resources.getJobStore().storeJobAndTrigger(jobDetail, trig);
notifySchedulerListenersJobAdded(jobDetail);
notifySchedulerThread(trigger.getNextFireTime().getTime());
notifySchedulerListenersSchduled(trigger);
return ft;
}
//存储给定的org.quartz.JobDetail和org.quartz.Trigger。
resources.getJobStore().storeJobAndTrigger(jobDetail, trig);
public void storeJobAndTrigger(final JobDetail newJob,
final OperableTrigger newTrigger)
throws JobPersistenceException {
executeInLock(
(isLockOnInsert()) ? LOCK_TRIGGER_ACCESS : null,
new VoidTransactionCallback() {
public void executeVoid(Connection conn) throws JobPersistenceException {
//(1)保存JobDetail
storeJob(conn, newJob, false);
//(2)保存Trigger
storeTrigger(conn, newTrigger, newJob, false,
Constants.STATE_WAITING, false, false);
}
});
}
//(1)保存JobDetail
storeJob(conn, newJob, false);
protected void storeJob(Connection conn,
JobDetail newJob, boolean replaceExisting)
throws JobPersistenceException {
//判断JobDetail是否已经存在,根据name和group
boolean existingJob = jobExists(conn, newJob.getKey());
try {
if (existingJob) {
if (!replaceExisting) {
throw new ObjectAlreadyExistsException(newJob);
}
//更新JobDetail
getDelegate().updateJobDetail(conn, newJob);
"UPDATE {0}JOB_DETAILS SET DESCRIPTION = ?, JOB_CLASS_NAME = ?, IS_DURABLE = ?, IS_NONCONCURRENT = ?, IS_UPDATE_DATA = ?, REQUESTS_RECOVERY = ?, JOB_DATA = ? WHERE SCHED_NAME = {1} AND JOB_NAME = ? AND JOB_GROUP = ?"
} else {
//插入JobDetail
getDelegate().insertJobDetail(conn, newJob);
"INSERT INTO {0}JOB_DETAILS (SCHED_NAME, JOB_NAME, JOB_GROUP, DESCRIPTION, JOB_CLASS_NAME, IS_DURABLE, IS_NONCONCURRENT, IS_UPDATE_DATA, REQUESTS_RECOVERY, JOB_DATA) VALUES({1}, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
}
} catch (IOException e) {
throw new JobPersistenceException("Couldn't store job: "
+ e.getMessage(), e);
} catch (SQLException e) {
throw new JobPersistenceException("Couldn't store job: "
+ e.getMessage(), e);
}
}
//(2)保存Trigger
storeTrigger(conn, newTrigger, newJob, false,
Constants.STATE_WAITING, false, false);
protected void storeTrigger(Connection conn,
OperableTrigger newTrigger, JobDetail job, boolean replaceExisting, String state,
boolean forceState, boolean recovering)
throws JobPersistenceException {
//判断Trigger是否已经存在,根据name和group
boolean existingTrigger = triggerExists(conn, newTrigger.getKey());
if ((existingTrigger) && (!replaceExisting)) {
throw new ObjectAlreadyExistsException(newTrigger);
}
try {
boolean shouldBepaused;
//进行一些状态的判断
if (!forceState) {
shouldBepaused = getDelegate().isTriggerGroupPaused(
conn, newTrigger.getKey().getGroup());
if(!shouldBepaused) {
shouldBepaused = getDelegate().isTriggerGroupPaused(conn,
ALL_GROUPS_PAUSED);
if (shouldBepaused) {
getDelegate().insertPausedTriggerGroup(conn, newTrigger.getKey().getGroup());
}
}
if (shouldBepaused && (state.equals(STATE_WAITING) || state.equals(STATE_ACQUIRED))) {
state = STATE_PAUSED;
}
}
//若job为null,重新获取!
if(job == null) {
job = getDelegate().selectJobDetail(conn, newTrigger.getJobKey(), getClassLoadHelper());
"SELECT * FROM {0}JOB_DETAILS WHERE SCHED_NAME = {1} AND JOB_NAME = ? AND JOB_GROUP = ?"
}
if (job == null) {
throw new JobPersistenceException("The job ("
+ newTrigger.getJobKey()
+ ") referenced by the trigger does not exist.");
}
//判断是否有DisallowConcurrentExecution注解,recovering恢复
if (job.isConcurrentExectionDisallowed() && !recovering) {
state = checkBlockedState(conn, job.getKey(), state);
"if (jobName != null) {
ps = conn.prepareStatement(rtp(SELECT_FIRED_TRIGGERS_OF_JOB));
"SELECT * FROM {0}FIRED_TRIGGERS WHERE SCHED_NAME = {1} AND JOB_NAME = ? AND JOB_GROUP = ?"
ps.setString(1, jobName);
ps.setString(2, groupName);
} else {
ps = conn
.prepareStatement(rtp(SELECT_FIRED_TRIGGERS_OF_JOB_GROUP));
"SELECT * FROM {0}FIRED_TRIGGERS WHERE SCHED_NAME = {1} AND JOB_GROUP = ?"
ps.setString(1, groupName);
}"
}
if (existingTrigger) {
//更新trigger
getDelegate().updateTrigger(conn, newTrigger, state, job);
" 大概贴出一些,具体想看完整的进入源码看
if(updateJobData) {
ps = conn.prepareStatement(rtp(UPDATE_TRIGGER));
"UPDATE {0}TRIGGERS SET JOB_NAME = ?, JOB_GROUP = ?, DESCRIPTION = ?, NEXT_FIRE_TIME = ?, PREV_FIRE_TIME = ?, TRIGGER_STATE = ?, TRIGGER_TYPE = ?, START_TIME = ?, END_TIME = ?, CALENDAR_NAME = ?, MISFIRE_INSTR = ?, PRIORITY = ?, JOB_DATA = ? WHERE SCHED_NAME = {1} AND TRIGGER_NAME = ? AND TRIGGER_GROUP = ?"
} else {
ps = conn.prepareStatement(rtp(UPDATE_TRIGGER_SKIP_DATA));
"UPDATE {0}TRIGGERS SET JOB_NAME = ?, JOB_GROUP = ?, DESCRIPTION = ?, NEXT_FIRE_TIME = ?, PREV_FIRE_TIME = ?, TRIGGER_STATE = ?, TRIGGER_TYPE = ?, START_TIME = ?, END_TIME = ?, CALENDAR_NAME = ?, MISFIRE_INSTR = ?, PRIORITY = ? WHERE SCHED_NAME = {1} AND TRIGGER_NAME = ? AND TRIGGER_GROUP = ?"
}
"
} else {
//插入trigger
getDelegate().insertTrigger(conn, newTrigger, state, job);
"INSERT INTO {0}TRIGGERS (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP, JOB_NAME, JOB_GROUP, DESCRIPTION, NEXT_FIRE_TIME, PREV_FIRE_TIME, TRIGGER_STATE, TRIGGER_TYPE, START_TIME, END_TIME, CALENDAR_NAME, MISFIRE_INSTR, JOB_DATA, PRIORITY) VALUES({1}, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
}
} catch (Exception e) {
throw new JobPersistenceException("Couldn't store trigger '" + newTrigger.getKey() + "' for '"
+ newTrigger.getJobKey() + "' job:" + e.getMessage(), e);
}
}
到这里,保存就结束了!
欢迎 一起讨论,学习!互相成长!
附学习博文地址:quartz2.2源码分析4-JobStore
欢迎访问我的csdn博客,我们一同成长!
"不管做什么,只要坚持下去就会看到不一样!在路上,不卑不亢!"
博客首页:http://blog.csdn.net/u010648555
Quartz源码——JobStore保存JonDetail和Trigger源码分析(一)的更多相关文章
- Python3.4 获取百度网页源码并保存在本地文件中
最近学习python 版本 3.4 抓取网页源码并且保存在本地文件中 import urllib.request url='http://www.baidu.com' #上面的url一定要写明确,如果 ...
- Quartz教程二:API,Job和Trigger
原文链接 | 译文链接 | 翻译:nkcoder | 校对:方腾飞 本系列教程由quartz-2.2.x官方文档翻译.整理而来,希望给同样对quartz感兴趣的朋友一些参考和帮助,有任何不当或错误之处 ...
- [Spark内核] 第32课:Spark Worker原理和源码剖析解密:Worker工作流程图、Worker启动Driver源码解密、Worker启动Executor源码解密等
本課主題 Spark Worker 原理 Worker 启动 Driver 源码鉴赏 Worker 启动 Executor 源码鉴赏 Worker 与 Master 的交互关系 [引言部份:你希望读者 ...
- Android生成二维码--保存和分享二维码图片
之前写过生成自定义二维码的两篇文章:<Android生成自定义二维码><Android生成二维码–拍照或从相册选取图片>,下面就介绍一下Android应用内如何保存以及分享二维 ...
- Quartz.Net系列(九):Trigger之DailyTimeIntervalScheduleBuilder详解
1.介绍 中文意义就是每日时间间隔计划生成 2.API讲解 (1)WithInterval.WithIntervalInHours.WithIntervalInMinutes.WithInterval ...
- div使用jqueryui 源码 | gridview多个功能的源码
div使用jqueryui 源码 | gridview多个功能的源码 一.gridview 选中行 改变颜色,双击选中 改变颜色 protected void gv1_SelectedIndexCha ...
- 编译哈工大语言技术平台云LTP(C++)源码及LTP4J(Java)源码
转自:编译哈工大语言技术平台云LTP(C++)源码及LTP4J(Java)源码 JDK:java version “1.8.0_31”Java(TM) SE Runtime Environment ( ...
- 【流媒体开发】VLC Media Player - Android 平台源码编译 与 二次开发详解 (提供详细800M下载好的编译源码及eclipse可调试播放器源码下载)
作者 : 韩曙亮 博客地址 : http://blog.csdn.net/shulianghan/article/details/42707293 转载请注明出处 : http://blog.csd ...
- 【安卓本卓】Android系统源码篇之(一)源码获取、源码目录结构及源码阅读工具简介
前言 古人常说,“熟读唐诗三百首,不会作诗也会吟”,说明了大量阅读诗歌名篇对学习作诗有非常大的帮助.做开发也一样,Android源码是全世界最优秀的Android工程师编写的代码,也是A ...
随机推荐
- 一文教你迅速解决分布式事务 XA 一致性问题
欢迎大家前往腾讯云技术社区,获取更多腾讯海量技术实践干货哦~ 作者:腾讯云数据库团队 近日,腾讯云发布了分布式数据库解决方案(DCDB),其最明显的特性之一就是提供了高于开源分布式事务XA的性能.大型 ...
- jQuery遍历-后代
后代是子.孙.曾孙等等. 通过 jQuery,您能够向下遍历 DOM 树,以查找元素的后代. 向下遍历 DOM 树 下面是两个用于向下遍历 DOM 树的 jQuery 方法: children() f ...
- centos 7 最小安装后 ip配置
安装玩CentOS7 后要进行 ip的配置 vi /etc/sysconfig/network-scripts/ifcfg-eth0 在里面输入 NAME=eth0 HWADDR=XX:XX:XX:X ...
- Thinkphp3.2.3框架下封装公共的函数,例如封装CURL函数来获取接口数据
当我们需要在控制层调用相同的封装函数时,写多次相同的函数,显得代码十分的拉杂,不精简: TP框架有一个很好的机制,可以再Common定义一个function.php函数,当我们在控制层调用的时候直接调 ...
- 教程,Python图片转字符堆叠图
Python 图片转字符画 一.实验说明 1. 环境登录 无需密码自动登录, 2. 环境介绍 本实验环境采用带桌面的UbuntuLinux环境,实验中会用到桌面上的程序: LX终端(LXTermina ...
- Vuforia开发完全指南---Vuforia概述
Vuforia概述 AR(Augmented Reality)增强现实,想必大家都已经很熟悉了.这是当下最热的技术之一,是利用计算机视觉和计算机图像学领域的相关知识将虚拟世界融入到现实生活当中.AR和 ...
- 必应词典英语学习APP案例分析
一.调研,评测 1.在此次作业之前并没有听过这个学英语app,必应听起来就像英语单词bing,第一次听到觉得这个app很奇怪,但没有将它和英语挂上钩.但是使用一阵子之后我觉得这个名字很好上口,其次这个 ...
- 201521123069 《Java程序设计》 第7周学习总结
1. 本周学习总结 以你喜欢的方式(思维导图或其他)归纳总结集合相关内容. 参考资料: XMind List列表:元素以线性方式存放,允许有重复的对象. Set集:集合中的对象不按特定的方式排序,并且 ...
- 201521123111《Java程序设计》第5周学习总结
1. 本章学习总结 你对于本章知识的学习总结 1.1 尝试使用思维导图总结有关多态与接口的知识点. 1.2 可选:使用常规方法总结其他上课内容. 2. 书面作业 1.代码阅读:Child压缩包内源代码 ...
- 201521123069 《Java程序设计》 第13周学习总结
1. 本周学习总结 以你喜欢的方式(思维导图.OneNote或其他)归纳总结多网络相关内容. 2. 书面作业 Q1. 网络基础 1.1 比较ping www.baidu.com与ping cec.jm ...