提交 88352a77 编写于 作者: X xueli.xue

版本大升级v1.2.0,关键字如下:1、任务组;2、“远程任务”、“本地任务”;3、“任务日志”;4、“串行执行”,并行执行;

上级 dc0011c1
...@@ -9,8 +9,8 @@ ...@@ -9,8 +9,8 @@
# DROP TABLE IF EXISTS XXL_JOB_QRTZ_TRIGGERS; # DROP TABLE IF EXISTS XXL_JOB_QRTZ_TRIGGERS;
# DROP TABLE IF EXISTS XXL_JOB_QRTZ_JOB_DETAILS; # DROP TABLE IF EXISTS XXL_JOB_QRTZ_JOB_DETAILS;
# DROP TABLE IF EXISTS XXL_JOB_QRTZ_CALENDARS; # DROP TABLE IF EXISTS XXL_JOB_QRTZ_CALENDARS;
# DROP TABLE IF EXISTS `xxl_job_qrtz_trigger_log`;
# DROP TABLE IF EXISTS `xxl_job_qrtz_trigger_info`; # DROP TABLE IF EXISTS `xxl_job_qrtz_trigger_info`;
# DROP TABLE IF EXISTS `xxl_job_qrtz_trigger_log`;
CREATE TABLE XXL_JOB_QRTZ_JOB_DETAILS CREATE TABLE XXL_JOB_QRTZ_JOB_DETAILS
( (
...@@ -157,29 +157,32 @@ CREATE TABLE XXL_JOB_QRTZ_LOCKS ...@@ -157,29 +157,32 @@ CREATE TABLE XXL_JOB_QRTZ_LOCKS
); );
CREATE TABLE `xxl_job_qrtz_trigger_log` ( DROP TABLE IF EXISTS `xxl_job_qrtz_trigger_info`;
CREATE TABLE `xxl_job_qrtz_trigger_info` (
`id` int(11) NOT NULL AUTO_INCREMENT, `id` int(11) NOT NULL AUTO_INCREMENT,
`job_group` varchar(255) NOT NULL COMMENT '任务组', `job_group` varchar(255) NOT NULL COMMENT '任务组',
`job_name` varchar(255) NOT NULL COMMENT '任务名', `job_name` varchar(255) NOT NULL COMMENT '任务名',
`job_cron` varchar(128) NOT NULL COMMENT '任务执行CORN表达式', `job_cron` varchar(128) NOT NULL COMMENT '任务执行CORN',
`job_desc` varchar(255) NOT NULL,
`job_class` varchar(255) NOT NULL COMMENT '任务执行JobBean', `job_class` varchar(255) NOT NULL COMMENT '任务执行JobBean',
`job_data` varchar(2048) DEFAULT NULL COMMENT '任务执行数据', `job_data` varchar(512) DEFAULT NULL COMMENT '任务执行数据',
`trigger_time` datetime DEFAULT NULL COMMENT '调度-时间', `add_time` datetime DEFAULT NULL,
`trigger_status` varchar(255) DEFAULT NULL COMMENT '调度-结果', `update_time` datetime DEFAULT NULL,
`trigger_msg` varchar(2048) DEFAULT NULL COMMENT '调度-日志', `author` varchar(64) DEFAULT NULL COMMENT '作者',
`handle_time` datetime DEFAULT NULL COMMENT '执行-时间', `alarm_email` varchar(255) DEFAULT NULL COMMENT '报警邮件',
`handle_status` varchar(255) DEFAULT NULL COMMENT '执行-状态', `alarm_threshold` int(11) DEFAULT NULL COMMENT '报警阀值(连续失败次数)',
`handle_msg` varchar(2048) DEFAULT NULL COMMENT '执行-日志',
PRIMARY KEY (`id`) PRIMARY KEY (`id`)
); );
DROP TABLE IF EXISTS `xxl_job_qrtz_trigger_log`;
CREATE TABLE `xxl_job_qrtz_trigger_log` ( CREATE TABLE `xxl_job_qrtz_trigger_log` (
`id` int(11) NOT NULL AUTO_INCREMENT, `id` int(11) NOT NULL AUTO_INCREMENT,
`job_group` varchar(255) NOT NULL COMMENT '任务组', `job_group` varchar(255) NOT NULL COMMENT '任务组',
`job_name` varchar(255) NOT NULL COMMENT '任务名', `job_name` varchar(255) NOT NULL COMMENT '任务名',
`job_cron` varchar(128) NOT NULL COMMENT '任务执行CORN表达式', `job_cron` varchar(128) NOT NULL COMMENT '任务执行CORN表达式',
`job_desc` varchar(255) NOT NULL,
`job_class` varchar(255) NOT NULL COMMENT '任务执行JobBean', `job_class` varchar(255) NOT NULL COMMENT '任务执行JobBean',
`job_data` varchar(2048) DEFAULT NULL COMMENT '任务执行数据', `job_data` varchar(512) DEFAULT NULL COMMENT '任务执行数据',
`trigger_time` datetime DEFAULT NULL COMMENT '调度-时间', `trigger_time` datetime DEFAULT NULL COMMENT '调度-时间',
`trigger_status` varchar(255) DEFAULT NULL COMMENT '调度-结果', `trigger_status` varchar(255) DEFAULT NULL COMMENT '调度-结果',
`trigger_msg` varchar(2048) DEFAULT NULL COMMENT '调度-日志', `trigger_msg` varchar(2048) DEFAULT NULL COMMENT '调度-日志',
......
...@@ -3,13 +3,12 @@ ...@@ -3,13 +3,12 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>com.xxl</groupId> <groupId>com.xxl</groupId>
<artifactId>xxl-job</artifactId> <artifactId>xxl-job</artifactId>
<version>1.1.1-SNAPSHOT</version> <version>1.2.0-SNAPSHOT</version>
<packaging>pom</packaging> <packaging>pom</packaging>
<modules> <modules>
<module>xxl-job-admin</module> <module>xxl-job-admin</module>
<module>xxl-job-client</module> <module>xxl-job-client</module>
<module>xxl-job-client-demo</module> <module>xxl-job-client-demo</module>
<module>xxl-job-simple</module>
</modules> </modules>
<build> <build>
......
...@@ -4,10 +4,10 @@ ...@@ -4,10 +4,10 @@
<parent> <parent>
<groupId>com.xxl</groupId> <groupId>com.xxl</groupId>
<artifactId>xxl-job</artifactId> <artifactId>xxl-job</artifactId>
<version>1.1.1-SNAPSHOT</version> <version>1.2.0-SNAPSHOT</version>
</parent> </parent>
<artifactId>xxl-job-admin</artifactId> <artifactId>xxl-job-admin</artifactId>
<version>1.1.2-SNAPSHOT</version> <version>1.2.1-SNAPSHOT</version>
<packaging>war</packaging> <packaging>war</packaging>
<properties> <properties>
...@@ -123,13 +123,6 @@ ...@@ -123,13 +123,6 @@
<version>5.1.29</version> <version>5.1.29</version>
</dependency> </dependency>
<!-- quartz :quartz-2.2.1/c3p0-0.9.1.1/slf4j-api-1.6.6 -->
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.2.2</version>
</dependency>
<!-- httpclient --> <!-- httpclient -->
<dependency> <dependency>
<groupId>org.apache.httpcomponents</groupId> <groupId>org.apache.httpcomponents</groupId>
...@@ -137,11 +130,18 @@ ...@@ -137,11 +130,18 @@
<version>4.3.6</version> <version>4.3.6</version>
</dependency> </dependency>
<!-- quartz :quartz-2.2.1/c3p0-0.9.1.1/slf4j-api-1.6.6 -->
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.2.2</version>
</dependency>
<!-- xxl-job-client --> <!-- xxl-job-client -->
<dependency> <dependency>
<groupId>com.xxl</groupId> <groupId>com.xxl</groupId>
<artifactId>xxl-job-client</artifactId> <artifactId>xxl-job-client</artifactId>
<version>1.1.2-SNAPSHOT</version> <version>1.2.1-SNAPSHOT</version>
</dependency> </dependency>
</dependencies> </dependencies>
......
...@@ -24,14 +24,15 @@ import com.xxl.job.core.model.ReturnT; ...@@ -24,14 +24,15 @@ import com.xxl.job.core.model.ReturnT;
import com.xxl.job.core.model.XxlJobInfo; import com.xxl.job.core.model.XxlJobInfo;
import com.xxl.job.core.util.DynamicSchedulerUtil; import com.xxl.job.core.util.DynamicSchedulerUtil;
import com.xxl.job.dao.IXxlJobInfoDao; import com.xxl.job.dao.IXxlJobInfoDao;
import com.xxl.job.service.job.HttpJobBean; import com.xxl.job.service.job.RemoteHttpJobBean;
import com.xxl.job.service.job.LocalJobBean; import com.xxl.job.service.job.impl.DemoConcurrentJobBean;
import com.xxl.job.service.job.LocalJobBeanB; import com.xxl.job.service.job.impl.DemoNomalJobBean;
/** /**
* index controller * index controller
* @author xuxueli 2015-12-19 16:13:16 * @author xuxueli 2015-12-19 16:13:16
*/ */
@SuppressWarnings("unchecked")
@Controller @Controller
@RequestMapping("/jobinfo") @RequestMapping("/jobinfo")
public class JobInfoController { public class JobInfoController {
...@@ -40,12 +41,12 @@ public class JobInfoController { ...@@ -40,12 +41,12 @@ public class JobInfoController {
private IXxlJobInfoDao xxlJobInfoDao; private IXxlJobInfoDao xxlJobInfoDao;
// remote job bean // remote job bean
public static Class <? extends Job> remoteJobBean = HttpJobBean.class; public static Class <? extends Job> remoteJobBean = RemoteHttpJobBean.class;
// loacal job bean // loacal job bean
public static List<Class <? extends Job>> localJobBeanList = new ArrayList<Class<? extends Job>>(); public static List<Class <? extends Job>> localJobBeanList = new ArrayList<Class<? extends Job>>();
static{ static{
localJobBeanList.add(LocalJobBean.class); localJobBeanList.add((Class<? extends Job>) DemoNomalJobBean.class);
localJobBeanList.add(LocalJobBeanB.class); localJobBeanList.add((Class<? extends Job>) DemoConcurrentJobBean.class);
} }
@RequestMapping @RequestMapping
...@@ -81,12 +82,11 @@ public class JobInfoController { ...@@ -81,12 +82,11 @@ public class JobInfoController {
return maps; return maps;
} }
@SuppressWarnings("unchecked")
@RequestMapping("/add") @RequestMapping("/add")
@ResponseBody @ResponseBody
public ReturnT<String> add(String jobGroup, String jobName, String jobCron, String jobDesc, String jobClass, public ReturnT<String> add(String jobGroup, String jobName, String jobCron, String jobDesc, String jobClass,
String handler_params, String handler_address, String handler_name, String handler_params, String handler_address, String handler_name,
String author, String alarm_email, int alarm_threshold) { String author, String alarmEmail, int alarmThreshold) {
// valid // valid
if (JobGroupEnum.match(jobGroup) == null) { if (JobGroupEnum.match(jobGroup) == null) {
...@@ -124,7 +124,7 @@ public class JobInfoController { ...@@ -124,7 +124,7 @@ public class JobInfoController {
if (StringUtils.isBlank(author)) { if (StringUtils.isBlank(author)) {
return new ReturnT<String>(500, "请输入“负责人”"); return new ReturnT<String>(500, "请输入“负责人”");
} }
if (StringUtils.isBlank(alarm_email)) { if (StringUtils.isBlank(alarmEmail)) {
return new ReturnT<String>(500, "请输入“报警邮件”"); return new ReturnT<String>(500, "请输入“报警邮件”");
} }
...@@ -137,6 +137,7 @@ public class JobInfoController { ...@@ -137,6 +137,7 @@ public class JobInfoController {
return new ReturnT<String>(500, "此任务已存在,请更换任务组或任务名"); return new ReturnT<String>(500, "此任务已存在,请更换任务组或任务名");
} }
// parse jobDataMap
HashMap<String, String> jobDataMap = new HashMap<String, String>(); HashMap<String, String> jobDataMap = new HashMap<String, String>();
jobDataMap.put(HandlerRepository.HANDLER_PARAMS, handler_params); jobDataMap.put(HandlerRepository.HANDLER_PARAMS, handler_params);
jobDataMap.put(HandlerRepository.HANDLER_ADDRESS, handler_address); jobDataMap.put(HandlerRepository.HANDLER_ADDRESS, handler_address);
...@@ -151,8 +152,8 @@ public class JobInfoController { ...@@ -151,8 +152,8 @@ public class JobInfoController {
jobInfo.setJobClass(jobClass); jobInfo.setJobClass(jobClass);
jobInfo.setJobData(JacksonUtil.writeValueAsString(jobDataMap)); jobInfo.setJobData(JacksonUtil.writeValueAsString(jobDataMap));
jobInfo.setAuthor(author); jobInfo.setAuthor(author);
jobInfo.setAlarmEmail(alarm_email); jobInfo.setAlarmEmail(alarmEmail);
jobInfo.setAlarmThreshold(alarm_threshold); jobInfo.setAlarmThreshold(alarmThreshold);
xxlJobInfoDao.save(jobInfo); xxlJobInfoDao.save(jobInfo);
try { try {
...@@ -174,7 +175,7 @@ public class JobInfoController { ...@@ -174,7 +175,7 @@ public class JobInfoController {
@ResponseBody @ResponseBody
public ReturnT<String> reschedule(String jobGroup, String jobName, String jobCron, String jobDesc, String jobClass, public ReturnT<String> reschedule(String jobGroup, String jobName, String jobCron, String jobDesc, String jobClass,
String handler_params, String handler_address, String handler_name, String handler_params, String handler_address, String handler_name,
String author, String alarm_email, int alarm_threshold) { String author, String alarmEmail, int alarmThreshold) {
// valid // valid
if (JobGroupEnum.match(jobGroup) == null) { if (JobGroupEnum.match(jobGroup) == null) {
...@@ -187,8 +188,19 @@ public class JobInfoController { ...@@ -187,8 +188,19 @@ public class JobInfoController {
return new ReturnT<String>(500, "“corn”不合法"); return new ReturnT<String>(500, "“corn”不合法");
} }
// parse jobDataMap
HashMap<String, String> jobDataMap = new HashMap<String, String>();
jobDataMap.put(HandlerRepository.HANDLER_PARAMS, handler_params);
jobDataMap.put(HandlerRepository.HANDLER_ADDRESS, handler_address);
jobDataMap.put(HandlerRepository.HANDLER_NAME, handler_name);
XxlJobInfo jobInfo = xxlJobInfoDao.load(jobGroup, jobName); XxlJobInfo jobInfo = xxlJobInfoDao.load(jobGroup, jobName);
jobInfo.setJobCron(jobCron); jobInfo.setJobCron(jobCron);
jobInfo.setJobDesc(jobDesc);
jobInfo.setJobData(JacksonUtil.writeValueAsString(jobDataMap));
jobInfo.setAuthor(author);
jobInfo.setAlarmEmail(alarmEmail);
jobInfo.setAlarmThreshold(alarmThreshold);
try { try {
// fresh quartz // fresh quartz
......
...@@ -71,7 +71,7 @@ public class XxlJobLogDaoImpl implements IXxlJobLogDao { ...@@ -71,7 +71,7 @@ public class XxlJobLogDaoImpl implements IXxlJobLogDao {
@Override @Override
public int updateTriggerInfo(XxlJobLog xxlJobLog) { public int updateTriggerInfo(XxlJobLog xxlJobLog) {
if (xxlJobLog!=null && xxlJobLog.getTriggerMsg().length()>2000) { if (xxlJobLog!=null && xxlJobLog.getTriggerMsg()!=null && xxlJobLog.getTriggerMsg().length()>2000) {
xxlJobLog.setTriggerMsg(xxlJobLog.getTriggerMsg().substring(0, 2000)); xxlJobLog.setTriggerMsg(xxlJobLog.getTriggerMsg().substring(0, 2000));
} }
return sqlSessionTemplate.update("XxlJobLogMapper.updateTriggerInfo", xxlJobLog); return sqlSessionTemplate.update("XxlJobLogMapper.updateTriggerInfo", xxlJobLog);
...@@ -79,7 +79,7 @@ public class XxlJobLogDaoImpl implements IXxlJobLogDao { ...@@ -79,7 +79,7 @@ public class XxlJobLogDaoImpl implements IXxlJobLogDao {
@Override @Override
public int updateHandleInfo(XxlJobLog xxlJobLog) { public int updateHandleInfo(XxlJobLog xxlJobLog) {
if (xxlJobLog!=null && xxlJobLog.getHandleMsg().length()>2000) { if (xxlJobLog!=null && xxlJobLog.getHandleMsg()!=null && xxlJobLog.getHandleMsg().length()>2000) {
xxlJobLog.setHandleMsg(xxlJobLog.getHandleMsg().substring(0, 2000)); xxlJobLog.setHandleMsg(xxlJobLog.getHandleMsg().substring(0, 2000));
} }
return sqlSessionTemplate.update("XxlJobLogMapper.updateHandleInfo", xxlJobLog); return sqlSessionTemplate.update("XxlJobLogMapper.updateHandleInfo", xxlJobLog);
......
package com.xxl.job.service.job;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.TimeUnit;
import org.quartz.DisallowConcurrentExecution;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.quartz.QuartzJobBean;
/**
* http job bean
* @author xuxueli 2015-12-17 18:20:34
*/
@DisallowConcurrentExecution // 串行;线程数要多配置几个,否则不生效;
public class LocalJobBean extends QuartzJobBean {
private static Logger logger = LoggerFactory.getLogger(LocalJobBean.class);
@Override
protected void executeInternal(JobExecutionContext context)
throws JobExecutionException {
String triggerKey = context.getTrigger().getKey().getName();
String triggerGroup = context.getTrigger().getKey().getGroup();
Map<String, Object> jobDataMap = context.getMergedJobDataMap().getWrappedMap();
// jobDataMap 2 params
Map<String, String> params = new HashMap<String, String>();
if (jobDataMap!=null && jobDataMap.size()>0) {
for (Entry<String, Object> item : jobDataMap.entrySet()) {
params.put(item.getKey(), String.valueOf(item.getValue()));
}
}
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
logger.info(">>>>>>>>>>> xxl-job run :jobId:{}, group:{}, jobDataMap:{}",
new Object[]{triggerKey, triggerGroup, jobDataMap});
}
}
\ No newline at end of file
package com.xxl.job.service.job;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.TimeUnit;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.quartz.QuartzJobBean;
/**
* http job bean
* @author xuxueli 2015-12-17 18:20:34
*/
public class LocalJobBeanB extends QuartzJobBean {
private static Logger logger = LoggerFactory.getLogger(LocalJobBeanB.class);
@Override
protected void executeInternal(JobExecutionContext context)
throws JobExecutionException {
String triggerKey = context.getTrigger().getKey().getName();
String triggerGroup = context.getTrigger().getKey().getGroup();
Map<String, Object> jobDataMap = context.getMergedJobDataMap().getWrappedMap();
// jobDataMap 2 params
Map<String, String> params = new HashMap<String, String>();
if (jobDataMap!=null && jobDataMap.size()>0) {
for (Entry<String, Object> item : jobDataMap.entrySet()) {
params.put(item.getKey(), String.valueOf(item.getValue()));
}
}
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
logger.info(">>>>>>>>>>> xxl-job run :jobId:{}, group:{}, jobDataMap:{}",
new Object[]{triggerKey, triggerGroup, jobDataMap});
}
}
\ No newline at end of file
package com.xxl.job.service.job;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.lang.StringUtils;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.JobKey;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.quartz.QuartzJobBean;
import com.xxl.job.client.handler.HandlerRepository;
import com.xxl.job.client.util.HttpUtil;
import com.xxl.job.client.util.JacksonUtil;
import com.xxl.job.core.model.XxlJobInfo;
import com.xxl.job.core.model.XxlJobLog;
import com.xxl.job.core.util.DynamicSchedulerUtil;
/**
* http job bean
* @author xuxueli 2015-12-17 18:20:34
*/
public abstract class LocalNomalJobBean extends QuartzJobBean {
private static Logger logger = LoggerFactory.getLogger(LocalNomalJobBean.class);
@Override
protected void executeInternal(JobExecutionContext context)
throws JobExecutionException {
JobKey jobKey = context.getTrigger().getJobKey();
XxlJobInfo jobInfo = DynamicSchedulerUtil.xxlJobInfoDao.load(jobKey.getGroup(), jobKey.getName());
@SuppressWarnings("unchecked")
HashMap<String, String> jobDataMap = (HashMap<String, String>) JacksonUtil.readValueRefer(jobInfo.getJobData(), Map.class);
// save log
XxlJobLog jobLog = new XxlJobLog();
jobLog.setJobGroup(jobInfo.getJobGroup());
jobLog.setJobName(jobInfo.getJobName());
jobLog.setJobCron(jobInfo.getJobCron());
jobLog.setJobDesc(jobInfo.getJobDesc());
jobLog.setJobClass(jobInfo.getJobClass());
jobLog.setJobData(jobInfo.getJobData());
jobLog.setJobClass(RemoteHttpJobBean.class.getName());
jobLog.setJobData(jobInfo.getJobData());
DynamicSchedulerUtil.xxlJobLogDao.save(jobLog);
logger.info(">>>>>>>>>>> xxl-job trigger start, jobLog:{}", jobLog);
// trigger request
String handler_params = jobDataMap.get(HandlerRepository.HANDLER_PARAMS);
String[] handlerParams = null;
if (StringUtils.isNotBlank(handler_params)) {
handlerParams = handler_params.split(",");
}
jobLog.setTriggerTime(new Date());
jobLog.setTriggerStatus(HttpUtil.SUCCESS);
jobLog.setTriggerMsg(null);
try {
Object responseMsg = this.handle(handlerParams);
jobLog.setHandleTime(new Date());
jobLog.setHandleStatus(HttpUtil.SUCCESS);
jobLog.setHandleMsg(JacksonUtil.writeValueAsString(responseMsg));
} catch (Exception e) {
logger.info("HandlerThread Exception:", e);
StringWriter out = new StringWriter();
e.printStackTrace(new PrintWriter(out));
jobLog.setHandleTime(new Date());
jobLog.setHandleStatus(HttpUtil.FAIL);
jobLog.setHandleMsg(out.toString());
}
// update trigger info
DynamicSchedulerUtil.xxlJobLogDao.updateTriggerInfo(jobLog);
DynamicSchedulerUtil.xxlJobLogDao.updateHandleInfo(jobLog);
logger.info(">>>>>>>>>>> xxl-job trigger end, jobLog.id:{}, jobLog:{}", jobLog.getId(), jobLog);
}
public abstract Object handle(String... param);
}
\ No newline at end of file
...@@ -27,8 +27,8 @@ import com.xxl.job.core.util.PropertiesUtil; ...@@ -27,8 +27,8 @@ import com.xxl.job.core.util.PropertiesUtil;
* @author xuxueli 2015-12-17 18:20:34 * @author xuxueli 2015-12-17 18:20:34
*/ */
@DisallowConcurrentExecution @DisallowConcurrentExecution
public class HttpJobBean extends QuartzJobBean { public class RemoteHttpJobBean extends QuartzJobBean {
private static Logger logger = LoggerFactory.getLogger(HttpJobBean.class); private static Logger logger = LoggerFactory.getLogger(RemoteHttpJobBean.class);
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Override @Override
...@@ -47,7 +47,7 @@ public class HttpJobBean extends QuartzJobBean { ...@@ -47,7 +47,7 @@ public class HttpJobBean extends QuartzJobBean {
jobLog.setJobClass(jobInfo.getJobClass()); jobLog.setJobClass(jobInfo.getJobClass());
jobLog.setJobData(jobInfo.getJobData()); jobLog.setJobData(jobInfo.getJobData());
jobLog.setJobClass(HttpJobBean.class.getName()); jobLog.setJobClass(RemoteHttpJobBean.class.getName());
jobLog.setJobData(jobInfo.getJobData()); jobLog.setJobData(jobInfo.getJobData());
DynamicSchedulerUtil.xxlJobLogDao.save(jobLog); DynamicSchedulerUtil.xxlJobLogDao.save(jobLog);
logger.info(">>>>>>>>>>> xxl-job trigger start, jobLog:{}", jobLog); logger.info(">>>>>>>>>>> xxl-job trigger start, jobLog:{}", jobLog);
......
triggerLogUrl=http://localhost:8080/xxl-job-admin/joblog/save trigger_log_url=http://localhost:8080/xxl-job-admin/joblog/save
\ No newline at end of file \ No newline at end of file
...@@ -148,11 +148,11 @@ ...@@ -148,11 +148,11 @@
<label for="lastname" class="col-sm-2 control-label">负责人</label> <label for="lastname" class="col-sm-2 control-label">负责人</label>
<div class="col-sm-4"><input type="text" class="form-control" name="author" placeholder="请输入“负责人”" maxlength="200" ></div> <div class="col-sm-4"><input type="text" class="form-control" name="author" placeholder="请输入“负责人”" maxlength="200" ></div>
<label for="lastname" class="col-sm-2 control-label">报警邮件</label> <label for="lastname" class="col-sm-2 control-label">报警邮件</label>
<div class="col-sm-4"><input type="text" class="form-control" name="alarm_email" placeholder="请输入“报警邮件”,多个邮件地址逗号分隔" maxlength="200" ></div> <div class="col-sm-4"><input type="text" class="form-control" name="alarmEmail" placeholder="请输入“报警邮件”,多个邮件地址逗号分隔" maxlength="200" ></div>
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="lastname" class="col-sm-2 control-label">报警阈值</label> <label for="lastname" class="col-sm-2 control-label">报警阈值</label>
<div class="col-sm-4"><input type="text" class="form-control" name="alarm_threshold" placeholder="请输入“报警阈值”" maxlength="200" ></div> <div class="col-sm-4"><input type="text" class="form-control" name="alarmThreshold" placeholder="请输入“报警阈值”" maxlength="200" ></div>
</div> </div>
<div class="form-group"> <div class="form-group">
<div class="col-sm-offset-3 col-sm-9"> <div class="col-sm-offset-3 col-sm-9">
...@@ -168,35 +168,49 @@ ...@@ -168,35 +168,49 @@
<!-- 更新.模态框 --> <!-- 更新.模态框 -->
<div class="modal fade" id="updateModal" tabindex="-1" role="dialog" aria-hidden="true"> <div class="modal fade" id="updateModal" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-dialog"> <div class="modal-dialog modal-lg">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
<h4 class="modal-title" >更新corn</h4> <h4 class="modal-title" >新增任务调度信息</h4>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<form class="form-horizontal form" role="form" > <form class="form-horizontal form" role="form" >
<div class="form-group"> <div class="form-group">
<label for="firstname" class="col-sm-3 control-label">任务Key</label> <label for="firstname" class="col-sm-2 control-label">任务组</label>
<div class="col-sm-9"><input type="text" class="form-control" name="triggerKeyName" placeholder="请输入任务Key" minlength="4" maxlength="100" readonly ></div> <div class="col-sm-4"><input type="text" class="form-control" name="jobGroup" placeholder="请输入“任务组”" minlength="4" maxlength="100" readonly ></div>
<label for="firstname" class="col-sm-2 control-label">任务名</label>
<div class="col-sm-4"><input type="text" class="form-control" name="jobName" placeholder="请输入“任务名”" minlength="4" maxlength="100" readonly ></div>
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="lastname" class="col-sm-3 control-label">任务Corn</label> <label for="lastname" class="col-sm-2 control-label">Corn</label>
<div class="col-sm-9"><input type="text" class="form-control" name="cronExpression" placeholder="请输入任务Corn" maxlength="100" ></div> <div class="col-sm-4"><input type="text" class="form-control" name="jobCron" placeholder="请输入“Corn”" maxlength="100" ></div>
<label for="lastname" class="col-sm-2 control-label">描述</label>
<div class="col-sm-4"><input type="text" class="form-control" name="jobDesc" placeholder="请输入“描述”" maxlength="200" ></div>
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="lastname" class="col-sm-3 control-label">任务描述</label> <label for="firstname" class="col-sm-2 control-label">JobBean</label>
<div class="col-sm-9"><input type="text" class="form-control" name="job_desc" placeholder="请输入任务描述" maxlength="200" ></div> <div class="col-sm-4"><input type="text" class="form-control" name="jobClass" placeholder="请输入“执行参数”" maxlength="100" readonly ></div>
<label for="firstname" class="col-sm-2 control-label">执行参数</label>
<div class="col-sm-4"><input type="text" class="form-control" name="handler_params" placeholder="请输入“执行参数”" maxlength="100" ></div>
</div>
<div class="form-group remote_panel">
<label for="lastname" class="col-sm-2 control-label">远程-机器地址</label>
<div class="col-sm-4"><input type="text" class="form-control" name="handler_address" placeholder="请输入“远程-机器地址”" maxlength="200" ></div>
<label for="lastname" class="col-sm-2 control-label">远程-执行器</label>
<div class="col-sm-4"><input type="text" class="form-control" name="handler_name" placeholder="请输入“远程-执行器”" maxlength="200" ></div>
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="lastname" class="col-sm-3 control-label">任务URL</label> <label for="lastname" class="col-sm-2 control-label">负责人</label>
<div class="col-sm-9"><input type="text" class="form-control" name="job_url" placeholder="请输入任务URL" maxlength="200" ></div> <div class="col-sm-4"><input type="text" class="form-control" name="author" placeholder="请输入“负责人”" maxlength="200" ></div>
<label for="lastname" class="col-sm-2 control-label">报警邮件</label>
<div class="col-sm-4"><input type="text" class="form-control" name="alarmEmail" placeholder="请输入“报警邮件”,多个邮件地址逗号分隔" maxlength="200" ></div>
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="lastname" class="col-sm-3 control-label">任务handler</label> <label for="lastname" class="col-sm-2 control-label">报警阈值</label>
<div class="col-sm-9"><input type="text" class="form-control" name="handleName" placeholder="请输入任务handler" maxlength="200" ></div> <div class="col-sm-4"><input type="text" class="form-control" name="alarmThreshold" placeholder="请输入“报警阈值”" maxlength="200" ></div>
</div> </div>
<div class="form-group"> <div class="form-group">
<div class="col-sm-offset-2 col-sm-10"> <div class="col-sm-offset-3 col-sm-9">
<button type="submit" class="btn btn-primary" >保存</button> <button type="submit" class="btn btn-primary" >保存</button>
<button type="button" class="btn btn-default" data-dismiss="modal">取消</button> <button type="button" class="btn btn-default" data-dismiss="modal">取消</button>
</div> </div>
......
...@@ -63,7 +63,9 @@ $(function() { ...@@ -63,7 +63,9 @@ $(function() {
if ('NORMAL' == data) { if ('NORMAL' == data) {
return '<small class="label label-success" ><i class="fa fa-clock-o"></i>'+ data +'</small>'; return '<small class="label label-success" ><i class="fa fa-clock-o"></i>'+ data +'</small>';
} else if ('PAUSED' == data){ } else if ('PAUSED' == data){
return '<small class="label label-default"><i class="fa fa-clock-o"></i>'+ data +'</small>'; return '<small class="label label-default" title="暂停" ><i class="fa fa-clock-o"></i>'+ data +'</small>';
} else if ('BLOCKED' == data){
return '<small class="label label-default" title="阻塞[串行]" ><i class="fa fa-clock-o"></i>'+ data +'</small>';
} }
return data; return data;
} }
...@@ -238,11 +240,11 @@ $(function() { ...@@ -238,11 +240,11 @@ $(function() {
required : true , required : true ,
maxlength: 200 maxlength: 200
}, },
alarm_email : { alarmEmail : {
required : true , required : true ,
maxlength: 200 maxlength: 200
}, },
alarm_threshold : { alarmThreshold : {
required : true , required : true ,
digits:true digits:true
} }
...@@ -273,11 +275,11 @@ $(function() { ...@@ -273,11 +275,11 @@ $(function() {
required : "请输入“负责人”." , required : "请输入“负责人”." ,
maxlength: "“负责人”长度不应超过50位" maxlength: "“负责人”长度不应超过50位"
}, },
alarm_email : { alarmEmail : {
required : "请输入“报警邮件”." , required : "请输入“报警邮件”." ,
maxlength: "“报警邮件”长度不应超过200位" maxlength: "“报警邮件”长度不应超过200位"
}, },
alarm_threshold : { alarmThreshold : {
required : "请输入“报警阈值”." , required : "请输入“报警阈值”." ,
digits:"阀值应该为整数." digits:"阀值应该为整数."
} }
...@@ -295,9 +297,8 @@ $(function() { ...@@ -295,9 +297,8 @@ $(function() {
submitHandler : function(form) { submitHandler : function(form) {
$.post(base_url + "/jobinfo/add", $("#addModal .form").serialize(), function(data, status) { $.post(base_url + "/jobinfo/add", $("#addModal .form").serialize(), function(data, status) {
if (data.code == "200") { if (data.code == "200") {
ComAlert.show(1, "新增调度任务成功", function(){ ComAlert.show(1, "新增任务成功", function(){
ComAlert.show(1, "新增任务成功"); window.location.reload();
jobTable.fnDraw();
}); });
} else { } else {
if (data.msg) { if (data.msg) {
...@@ -310,29 +311,44 @@ $(function() { ...@@ -310,29 +311,44 @@ $(function() {
} }
}); });
$("#addModal").on('hide.bs.modal', function () { $("#addModal").on('hide.bs.modal', function () {
//$("#addModal .form")[0].reset(); $("#addModal .form")[0].reset();
addModalValidate.resetForm(); addModalValidate.resetForm();
$("#addModal .form .form-group").removeClass("has-error"); $("#addModal .form .form-group").removeClass("has-error");
$(".remote_panel").show(); // remote
}); });
// 远程任务/本地任务,切换 // 远程任务/本地任务,切换
$("#addModal select[name='jobClass']").change(function() { $("#addModal select[name='jobClass']").change(function() {
//console.log($(this).val()); //console.log( $(this).val().indexOf('RemoteHttpJobBean') );
console.log( $(this).val().indexOf('HttpJobBean') );
if($(this).val().indexOf('HttpJobBean') > -1){ if($(this).val().indexOf('RemoteHttpJobBean') > -1){
$(".remote_panel").show(); // remote $(".remote_panel").show(); // remote
} else if($(this).val().indexOf('HttpJobBean') == -1){ } else if($(this).val().indexOf('RemoteHttpJobBean') == -1){
$(".remote_panel").hide(); // local $(".remote_panel").hide(); // local
} }
}); });
// 更新 // 更新
$("#job_list").on('click', '.update',function() { $("#job_list").on('click', '.update',function() {
$("#updateModal .form input[name='triggerKeyName']").val($(this).parent('p').attr("jobName")); $("#updateModal .form input[name='jobGroup']").val($(this).parent('p').attr("jobGroup"));
$("#updateModal .form input[name='cronExpression']").val($(this).parent('p').attr("cronExpression")); $("#updateModal .form input[name='jobName']").val($(this).parent('p').attr("jobName"));
$("#updateModal .form input[name='job_desc']").val($(this).parent('p').attr("job_desc")); $("#updateModal .form input[name='jobCron']").val($(this).parent('p').attr("jobCron"));
$("#updateModal .form input[name='job_url']").val($(this).parent('p').attr("job_url")); $("#updateModal .form input[name='jobDesc']").val($(this).parent('p').attr("jobDesc"));
$("#updateModal .form input[name='handleName']").val($(this).parent('p').attr("handleName")); $("#updateModal .form input[name='jobClass']").val($(this).parent('p').attr("jobClass"));
$("#updateModal .form input[name='handler_params']").val($(this).parent('p').attr("handler_params"));
$("#updateModal .form input[name='handler_address']").val($(this).parent('p').attr("handler_address"));
$("#updateModal .form input[name='handler_name']").val($(this).parent('p').attr("handler_name"));
$("#updateModal .form input[name='author']").val($(this).parent('p').attr("author"));
$("#updateModal .form input[name='alarmEmail']").val($(this).parent('p').attr("alarmEmail"));
$("#updateModal .form input[name='alarmThreshold']").val($(this).parent('p').attr("alarmThreshold"));
var _jobClass = $(this).parent('p').attr("jobClass");
if(_jobClass.indexOf('RemoteHttpJobBean') > -1){
$(".remote_panel").show(); // remote
} else if($(this).val().indexOf('RemoteHttpJobBean') == -1){
$(".remote_panel").hide(); // local
}
$('#updateModal').modal({backdrop: false, keyboard: false}).modal('show'); $('#updateModal').modal({backdrop: false, keyboard: false}).modal('show');
}); });
var updateModalValidate = $("#updateModal .form").validate({ var updateModalValidate = $("#updateModal .form").validate({
...@@ -340,49 +356,63 @@ $(function() { ...@@ -340,49 +356,63 @@ $(function() {
errorClass : 'help-block', errorClass : 'help-block',
focusInvalid : true, focusInvalid : true,
rules : { rules : {
triggerKeyName : { jobCron : {
required : true ,
minlength: 4,
maxlength: 100
},
cronExpression : {
required : true , required : true ,
maxlength: 100 maxlength: 100
}, },
job_desc : { jobDesc : {
required : true , required : true ,
maxlength: 200 maxlength: 200
}, },
job_url : { handler_address : {
required : true , required : true ,
maxlength: 200 maxlength: 200
}, },
handleName : { handler_name : {
required : true , required : true ,
maxlength: 200 maxlength: 200
},
author : {
required : true ,
maxlength: 200
},
alarmEmail : {
required : true ,
maxlength: 200
},
alarmThreshold : {
required : true ,
digits:true
} }
}, },
messages : { messages : {
triggerKeyName : { jobCron : {
required :"请输入“任务Key”." , required :"请输入“Corn”." ,
minlength:"“任务Key”不应低于4位", maxlength:"“Corn”长度不应超过100位"
maxlength:"“任务Key”不应超过100位"
},
cronExpression : {
required :"请输入“任务Corn”." ,
maxlength:"“任务Corn”不应超过100位"
}, },
job_desc : { jobDesc : {
required :"请输入“任务描述”." , required :"请输入“任务描述”." ,
maxlength:"“任务描述”长度不应超过200位" maxlength:"“任务描述”长度不应超过200位"
}, },
job_url : { handler_address : {
required :"请输入“任务URL”." , required :"请输入“远程-机器地址”." ,
maxlength:"任务URL”长度不应超过200位" maxlength:"远程-机器地址”长度不应超过200位"
}, },
handleName : { handler_name : {
required : "请输入“任务handler”." , required : "请输入“远程-执行器”." ,
maxlength: "“任务handler”长度不应超过200位" maxlength: "“远程-执行器”长度不应超过200位"
},
author : {
required : "请输入“负责人”." ,
maxlength: "“负责人”长度不应超过50位"
},
alarmEmail : {
required : "请输入“报警邮件”." ,
maxlength: "“报警邮件”长度不应超过200位"
},
alarmThreshold : {
required : "请输入“报警阈值”." ,
digits:"阀值应该为整数."
} }
}, },
highlight : function(element) { highlight : function(element) {
...@@ -396,7 +426,7 @@ $(function() { ...@@ -396,7 +426,7 @@ $(function() {
element.parent('div').append(error); element.parent('div').append(error);
}, },
submitHandler : function(form) { submitHandler : function(form) {
$.post(base_url + "/job/reschedule", $("#updateModal .form").serialize(), function(data, status) { $.post(base_url + "/jobinfo/reschedule", $("#updateModal .form").serialize(), function(data, status) {
if (data.code == "200") { if (data.code == "200") {
ComAlert.show(1, "更新成功", function(){ ComAlert.show(1, "更新成功", function(){
window.location.reload(); window.location.reload();
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
<wb-resource deploy-path="/" source-path="/src/main/webapp" tag="defaultRootSource"/> <wb-resource deploy-path="/" source-path="/src/main/webapp" tag="defaultRootSource"/>
<wb-resource deploy-path="/WEB-INF/classes" source-path="/src/main/java"/> <wb-resource deploy-path="/WEB-INF/classes" source-path="/src/main/java"/>
<wb-resource deploy-path="/WEB-INF/classes" source-path="/src/main/resources"/> <wb-resource deploy-path="/WEB-INF/classes" source-path="/src/main/resources"/>
<dependent-module archiveName="xxl-job-client-1.1.2-SNAPSHOT.jar" deploy-path="/WEB-INF/lib" handle="module:/resource/xxl-job-client/xxl-job-client"> <dependent-module archiveName="xxl-job-client-1.2.1-SNAPSHOT.jar" deploy-path="/WEB-INF/lib" handle="module:/resource/xxl-job-client/xxl-job-client">
<dependency-type>uses</dependency-type> <dependency-type>uses</dependency-type>
</dependent-module> </dependent-module>
<property name="context-root" value="xxl-job-client-demo"/> <property name="context-root" value="xxl-job-client-demo"/>
......
...@@ -4,10 +4,10 @@ ...@@ -4,10 +4,10 @@
<parent> <parent>
<groupId>com.xxl</groupId> <groupId>com.xxl</groupId>
<artifactId>xxl-job</artifactId> <artifactId>xxl-job</artifactId>
<version>1.1.1-SNAPSHOT</version> <version>1.2.0-SNAPSHOT</version>
</parent> </parent>
<artifactId>xxl-job-client-demo</artifactId> <artifactId>xxl-job-client-demo</artifactId>
<version>1.1.2-SNAPSHOT</version> <version>1.2.1-SNAPSHOT</version>
<packaging>war</packaging> <packaging>war</packaging>
<properties> <properties>
...@@ -55,7 +55,7 @@ ...@@ -55,7 +55,7 @@
<dependency> <dependency>
<groupId>com.xxl</groupId> <groupId>com.xxl</groupId>
<artifactId>xxl-job-client</artifactId> <artifactId>xxl-job-client</artifactId>
<version>1.1.2-SNAPSHOT</version> <version>1.2.1-SNAPSHOT</version>
</dependency> </dependency>
</dependencies> </dependencies>
......
...@@ -4,10 +4,10 @@ ...@@ -4,10 +4,10 @@
<parent> <parent>
<groupId>com.xxl</groupId> <groupId>com.xxl</groupId>
<artifactId>xxl-job</artifactId> <artifactId>xxl-job</artifactId>
<version>1.1.1-SNAPSHOT</version> <version>1.2.0-SNAPSHOT</version>
</parent> </parent>
<artifactId>xxl-job-client</artifactId> <artifactId>xxl-job-client</artifactId>
<version>1.1.2-SNAPSHOT</version> <version>1.2.1-SNAPSHOT</version>
<dependencies> <dependencies>
......
...@@ -43,14 +43,20 @@ public class HandlerRepository { ...@@ -43,14 +43,20 @@ public class HandlerRepository {
String _msg = ""; String _msg = "";
// push data to queue // push data to queue
HandlerThread handlerThread = handlerTreadMap.get(_param.get(HandlerRepository.HANDLER_NAME)); String handler_name = _param.get(HandlerRepository.HANDLER_NAME);
if (handlerThread != null) { if (handler_name!=null && handler_name.trim().length()>0) {
handlerThread.pushData(_param); HandlerThread handlerThread = handlerTreadMap.get(handler_name);
_status = HttpUtil.SUCCESS; if (handlerThread != null) {
} else { handlerThread.pushData(_param);
_msg = "handler not found."; _status = HttpUtil.SUCCESS;
} else {
_msg = "handler not found.";
}
}else{
_msg = "param[HANDLER_NAME] not exists.";
} }
HashMap<String, String> triggerData = new HashMap<String, String>(); HashMap<String, String> triggerData = new HashMap<String, String>();
triggerData.put(HandlerRepository.TRIGGER_LOG_ID, _param.get(HandlerRepository.TRIGGER_LOG_ID)); triggerData.put(HandlerRepository.TRIGGER_LOG_ID, _param.get(HandlerRepository.TRIGGER_LOG_ID));
triggerData.put(HttpUtil.status, _status); triggerData.put(HttpUtil.status, _status);
......
...@@ -35,60 +35,62 @@ public class HandlerThread extends Thread{ ...@@ -35,60 +35,62 @@ public class HandlerThread extends Thread{
int i = 1; int i = 1;
@Override @Override
public void run() { public void run() {
try { while(true){
i++; try {
Map<String, String> handlerData = handlerDataQueue.poll(); i++;
if (handlerData!=null) { Map<String, String> handlerData = handlerDataQueue.poll();
String trigger_log_url = handlerData.get(HandlerRepository.TRIGGER_LOG_URL); if (handlerData!=null) {
String trigger_log_id = handlerData.get(HandlerRepository.TRIGGER_LOG_ID); String trigger_log_url = handlerData.get(HandlerRepository.TRIGGER_LOG_URL);
String handler_params = handlerData.get(HandlerRepository.HANDLER_PARAMS); String trigger_log_id = handlerData.get(HandlerRepository.TRIGGER_LOG_ID);
String handler_params = handlerData.get(HandlerRepository.HANDLER_PARAMS);
// parse param
String[] handlerParams = null;
if (handler_params!=null && handler_params.trim().length()>0) {
handlerParams = handler_params.split(",");
} else {
handlerParams = new String[0];
}
// handle job
JobHandleStatus _status = JobHandleStatus.FAIL;
String _msg = null;
try {
_status = handler.handle(handlerParams);
} catch (Exception e) {
logger.info("HandlerThread Exception:", e);
StringWriter out = new StringWriter();
e.printStackTrace(new PrintWriter(out));
_msg = out.toString();
}
// callback handler info
String callback_response[] = null;
try {
HashMap<String, String> params = new HashMap<String, String>(); // parse param
params.put(HandlerRepository.TRIGGER_LOG_ID, trigger_log_id); String[] handlerParams = null;
params.put(HttpUtil.status, _status.name()); if (handler_params!=null && handler_params.trim().length()>0) {
params.put(HttpUtil.msg, _msg); handlerParams = handler_params.split(",");
callback_response = HttpUtil.post(trigger_log_url, params); } else {
} catch (Exception e) { handlerParams = new String[0];
logger.info("HandlerThread Exception:", e); }
}
logger.info("<<<<<<<<<<< xxl-job thread handle, handlerData:{}, callback_status:{}, callback_msg:{}, callback_response:{}, thread:{}", // handle job
new Object[]{handlerData, _status, _msg, callback_response, this}); JobHandleStatus _status = JobHandleStatus.FAIL;
} else { String _msg = null;
try { try {
TimeUnit.MILLISECONDS.sleep(i * 100); _status = handler.handle(handlerParams);
} catch (InterruptedException e) { } catch (Exception e) {
e.printStackTrace(); logger.info("HandlerThread Exception:", e);
} StringWriter out = new StringWriter();
if (i>5) { e.printStackTrace(new PrintWriter(out));
i= 0; _msg = out.toString();
}
// callback handler info
String callback_response[] = null;
try {
HashMap<String, String> params = new HashMap<String, String>();
params.put(HandlerRepository.TRIGGER_LOG_ID, trigger_log_id);
params.put(HttpUtil.status, _status.name());
params.put(HttpUtil.msg, _msg);
callback_response = HttpUtil.post(trigger_log_url, params);
} catch (Exception e) {
logger.info("HandlerThread Exception:", e);
}
logger.info("<<<<<<<<<<< xxl-job thread handle, handlerData:{}, callback_status:{}, callback_msg:{}, callback_response:{}, thread:{}",
new Object[]{handlerData, _status, _msg, callback_response, this});
} else {
try {
TimeUnit.MILLISECONDS.sleep(i * 100);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (i>5) {
i= 0;
}
} }
} catch (Exception e) {
logger.info("HandlerThread Exception:", e);
} }
} catch (Exception e) {
logger.info("HandlerThread Exception:", e);
} }
} }
} }
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" output="target/classes" path="src/main/java">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry excluding="**" kind="src" output="target/classes" path="src/main/resources">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="src" output="target/test-classes" path="src/test/java">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
<attributes>
<attribute name="maven.pomderived" value="true"/>
<attribute name="org.eclipse.jst.component.dependency" value="/WEB-INF/lib"/>
</attributes>
</classpathentry>
<classpathentry kind="output" path="target/classes"/>
</classpath>
/target/
/.settings/
/bin/
/.gitignore
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>xxl-job-simple</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.wst.jsdt.core.javascriptValidator</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.wst.common.project.facet.core.builder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.wst.validation.validationbuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.m2e.core.maven2Builder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jem.workbench.JavaEMFNature</nature>
<nature>org.eclipse.wst.common.modulecore.ModuleCoreNature</nature>
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>org.eclipse.m2e.core.maven2Nature</nature>
<nature>org.eclipse.wst.common.project.facet.core.nature</nature>
<nature>org.eclipse.wst.jsdt.core.jsNature</nature>
</natures>
</projectDescription>
eclipse.preferences.version=1
encoding//src/main/java=UTF8
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
org.eclipse.jdt.core.compiler.compliance=1.6
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
org.eclipse.jdt.core.compiler.source=1.6
<?xml version="1.0" encoding="UTF-8"?><project-modules id="moduleCoreId" project-version="1.5.0">
<wb-module deploy-name="xxl-job-simple">
<wb-resource deploy-path="/" source-path="/target/m2e-wtp/web-resources"/>
<wb-resource deploy-path="/" source-path="/src/main/webapp" tag="defaultRootSource"/>
<wb-resource deploy-path="/WEB-INF/classes" source-path="/src/main/java"/>
<wb-resource deploy-path="/WEB-INF/classes" source-path="/src/main/resources"/>
<property name="context-root" value="xxl-job-simple"/>
<property name="java-output-path" value="/xxl-job-simple/target/classes"/>
</wb-module>
</project-modules>
<?xml version="1.0" encoding="UTF-8"?>
<faceted-project>
<fixed facet="wst.jsdt.web"/>
<installed facet="java" version="1.6"/>
<installed facet="jst.web" version="2.5"/>
<installed facet="wst.jsdt.web" version="1.0"/>
</faceted-project>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.xxl</groupId>
<artifactId>xxl-job</artifactId>
<version>1.1.1-SNAPSHOT</version>
</parent>
<artifactId>xxl-job-simple</artifactId>
<version>1.1.2-SNAPSHOT</version>
<packaging>war</packaging>
<properties>
<spring.version>3.2.14.RELEASE</spring.version>
</properties>
<dependencies>
<!-- springframe start -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- springframe end -->
<!-- aspectjweaver (support spring aop) -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.7</version>
</dependency>
<!-- jackson (support spring json) -->
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.9.13</version>
</dependency>
<!-- slf4j -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.5</version>
</dependency>
<!-- freemarker -->
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.20</version>
</dependency>
<!-- commons-beanutils -->
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
<version>1.9.2</version>
</dependency>
<!-- commons-lang -->
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>2.6</version>
</dependency>
<!-- servlet -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.1</version>
<scope>provided</scope>
</dependency>
<!-- junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<!-- c3p0 -->
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.1.2</version>
</dependency>
<!-- mybatis-spring -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.2.2</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.2.8</version>
</dependency>
<!-- mysql-connector -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.29</version>
</dependency>
<!-- quartz :quartz-2.2.1/c3p0-0.9.1.1/slf4j-api-1.6.6 -->
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.2.1</version>
</dependency>
<!-- httpclient -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.3.6</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.6</source>
<target>1.6</target>
<encoding>UTF8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>2.2</version>
<configuration>
<archiveClasses>true</archiveClasses>
</configuration>
</plugin>
</plugins>
</build>
</project>
package com.xxl.job.controller;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.lang.StringUtils;
import org.quartz.CronExpression;
import org.quartz.Job;
import org.quartz.SchedulerException;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import com.xxl.job.core.model.ReturnT;
import com.xxl.job.core.util.DynamicSchedulerUtil;
import com.xxl.job.service.job.DemoJobBean;
import com.xxl.job.service.job.DemoJobBeanB;
/**
* index controller
* @author xuxueli 2015-12-19 16:13:16
*/
@Controller
public class IndexController {
// local job bean list
public static Map<String, String> jobBeanMap = new HashMap<String, String>();
@RequestMapping("")
public String index(Model model) {
jobBeanMap.put(DemoJobBean.class.getName(), "测试任务");
jobBeanMap.put(DemoJobBeanB.class.getName(), "测试任务B");
model.addAttribute("jobBeanMap", jobBeanMap);
// job list
List<Map<String, Object>> jobList = DynamicSchedulerUtil.getJobList();
model.addAttribute("jobList", jobList);
return "job/index";
}
@RequestMapping("/help")
public String help(Model model) {
return "job/help";
}
@SuppressWarnings("unchecked")
@RequestMapping("/job/add")
@ResponseBody
public ReturnT<String> add(HttpServletRequest request) {
String triggerKeyName = null;
String cronExpression = null;
Map<String, Object> jobData = new HashMap<String, Object>();
try {
request.setCharacterEncoding("utf-8");
} catch (UnsupportedEncodingException e1) {
e1.printStackTrace();
}
Set<Map.Entry<String, String[]>> paramSet = request.getParameterMap().entrySet();
for (Entry<String, String[]> param : paramSet) {
if (param.getKey().equals("triggerKeyName")) {
triggerKeyName = param.getValue()[0];
} else if (param.getKey().equals("cronExpression")) {
cronExpression = param.getValue()[0];
} else {
jobData.put(param.getKey(), param.getValue().length>0?param.getValue()[0]:param.getValue());
}
}
// triggerKeyName
if (StringUtils.isBlank(triggerKeyName)) {
return new ReturnT<String>(500, "请输入“任务key”");
}
// cronExpression
if (StringUtils.isBlank(cronExpression)) {
return new ReturnT<String>(500, "请输入“任务corn”");
}
if (!CronExpression.isValidExpression(cronExpression)) {
return new ReturnT<String>(500, "“任务corn”不合法");
}
// jobData
if (jobData.get(DynamicSchedulerUtil.job_desc)==null || jobData.get(DynamicSchedulerUtil.job_desc).toString().trim().length()==0) {
return new ReturnT<String>(500, "请输入“任务描述”");
}
// job_bean
String job_bean = (String) jobData.get(DynamicSchedulerUtil.job_bean);
if (StringUtils.isBlank(job_bean)) {
return new ReturnT<String>(500, "JobBean不可为空");
}
// jobClass
Class<? extends Job> jobClass = null;
try {
Class<?> clazz = Class.forName(job_bean);
if (clazz!=null) {
jobClass = (Class<? extends Job>) clazz;
}
} catch (ClassNotFoundException e1) {
e1.printStackTrace();
}
if (jobClass == null) {
return new ReturnT<String>(500, "JobBean未知");
}
try {
boolean result = DynamicSchedulerUtil.addJob(triggerKeyName, cronExpression, jobClass, jobData);
if (!result) {
return new ReturnT<String>(500, "任务ID重复,请更换确认");
}
return ReturnT.SUCCESS;
} catch (SchedulerException e) {
e.printStackTrace();
}
return ReturnT.FAIL;
}
@RequestMapping("/job/reschedule")
@ResponseBody
public ReturnT<String> reschedule(String triggerKeyName, String cronExpression) {
// triggerKeyName
if (StringUtils.isBlank(triggerKeyName)) {
return new ReturnT<String>(500, "请输入“任务key”");
}
// cronExpression
if (StringUtils.isBlank(cronExpression)) {
return new ReturnT<String>(500, "请输入“任务corn”");
}
if (!CronExpression.isValidExpression(cronExpression)) {
return new ReturnT<String>(500, "“任务corn”不合法");
}
try {
DynamicSchedulerUtil.rescheduleJob(triggerKeyName, cronExpression);
return ReturnT.SUCCESS;
} catch (SchedulerException e) {
e.printStackTrace();
}
return ReturnT.FAIL;
}
@RequestMapping("/job/remove")
@ResponseBody
public ReturnT<String> remove(String triggerKeyName) {
try {
DynamicSchedulerUtil.removeJob(triggerKeyName);
return ReturnT.SUCCESS;
} catch (SchedulerException e) {
e.printStackTrace();
return ReturnT.FAIL;
}
}
@RequestMapping("/job/pause")
@ResponseBody
public ReturnT<String> pause(String triggerKeyName) {
try {
DynamicSchedulerUtil.pauseJob(triggerKeyName);
return ReturnT.SUCCESS;
} catch (SchedulerException e) {
e.printStackTrace();
return ReturnT.FAIL;
}
}
@RequestMapping("/job/resume")
@ResponseBody
public ReturnT<String> resume(String triggerKeyName) {
try {
DynamicSchedulerUtil.resumeJob(triggerKeyName);
return ReturnT.SUCCESS;
} catch (SchedulerException e) {
e.printStackTrace();
return ReturnT.FAIL;
}
}
@RequestMapping("/job/trigger")
@ResponseBody
public ReturnT<String> triggerJob(String triggerKeyName) {
try {
DynamicSchedulerUtil.triggerJob(triggerKeyName);
return ReturnT.SUCCESS;
} catch (SchedulerException e) {
e.printStackTrace();
return ReturnT.FAIL;
}
}
}
package com.xxl.job.core.model;
/**
* common return
* @author xuxueli 2015-12-4 16:32:31
* @param <T>
*/
public class ReturnT<T> {
public static final ReturnT<String> SUCCESS = new ReturnT<String>(null);
public static final ReturnT<String> FAIL = new ReturnT<String>(500, null);
private int code;
private String msg;
private T content;
public ReturnT(int code, String msg) {
this.code = code;
this.msg = msg;
}
public ReturnT(T content) {
this.code = 200;
this.content = content;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public T getContent() {
return content;
}
public void setContent(T content) {
this.content = content;
}
@Override
public String toString() {
return "ReturnT [code=" + code + ", msg=" + msg + ", content="
+ content + "]";
}
}
package com.xxl.job.core.model;
import java.util.Date;
/**
* xxl-job log, used to track trigger process
* @author xuxueli 2015-12-19 23:19:09
*/
public class XxlJobLog {
private String jobTriggerUuid;
private String jobHandleName;
// trigger info
private Date triggerTime;
private String triggerStatus;
private String triggerDetailLog;
// handle info
private Date handleTime;
private String handleStatus;
private String handleDetailLog;
public String getJobTriggerUuid() {
return jobTriggerUuid;
}
public void setJobTriggerUuid(String jobTriggerUuid) {
this.jobTriggerUuid = jobTriggerUuid;
}
public String getJobHandleName() {
return jobHandleName;
}
public void setJobHandleName(String jobHandleName) {
this.jobHandleName = jobHandleName;
}
public Date getTriggerTime() {
return triggerTime;
}
public void setTriggerTime(Date triggerTime) {
this.triggerTime = triggerTime;
}
public String getTriggerStatus() {
return triggerStatus;
}
public void setTriggerStatus(String triggerStatus) {
this.triggerStatus = triggerStatus;
}
public String getTriggerDetailLog() {
return triggerDetailLog;
}
public void setTriggerDetailLog(String triggerDetailLog) {
this.triggerDetailLog = triggerDetailLog;
}
public Date getHandleTime() {
return handleTime;
}
public void setHandleTime(Date handleTime) {
this.handleTime = handleTime;
}
public String getHandleStatus() {
return handleStatus;
}
public void setHandleStatus(String handleStatus) {
this.handleStatus = handleStatus;
}
public String getHandleDetailLog() {
return handleDetailLog;
}
public void setHandleDetailLog(String handleDetailLog) {
this.handleDetailLog = handleDetailLog;
}
@Override
public String toString() {
return "XxlJobLog [jobTriggerUuid=" + jobTriggerUuid + ", jobHandleName=" + jobHandleName
+ ", triggerTime=" + triggerTime + ", triggerStatus=" + triggerStatus + ", triggerDetailLog="
+ triggerDetailLog + ", handleTime=" + handleTime + ", handleStatus=" + handleStatus
+ ", handleDetailLog=" + handleDetailLog + "]";
}
}
package com.xxl.job.core.util;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.quartz.CronScheduleBuilder;
import org.quartz.CronTrigger;
import org.quartz.Job;
import org.quartz.JobBuilder;
import org.quartz.JobDataMap;
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.matchers.GroupMatcher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert;
/**
* base quartz scheduler util
* @author xuxueli 2015-12-19 16:13:53
*/
public final class DynamicSchedulerUtil implements InitializingBean {
private static final Logger logger = LoggerFactory.getLogger(DynamicSchedulerUtil.class);
// Scheduler
private static Scheduler scheduler;
public static void setScheduler(Scheduler scheduler) {
DynamicSchedulerUtil.scheduler = scheduler;
}
@Override
public void afterPropertiesSet() throws Exception {
Assert.notNull(scheduler, "quartz scheduler is null");
logger.info(">>>>>>>>> init quartz scheduler success.[{}]", scheduler);
}
// getJobKeys
public static List<Map<String, Object>> getJobList(){
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) {
e.printStackTrace();
return null;
}
return jobList;
}
public static final String job_desc = "job_desc";
public static final String job_bean = "job_bean";
// addJob 新增
public static boolean addJob(String triggerKeyName, String cronExpression, Class<? extends Job> jobClass, Map<String, Object> jobData) throws SchedulerException {
// TriggerKey : name + group
String group = Scheduler.DEFAULT_GROUP;
TriggerKey triggerKey = TriggerKey.triggerKey(triggerKeyName, group);
// TriggerKey valid if_exists
if (scheduler.checkExists(triggerKey)) {
Trigger trigger = scheduler.getTrigger(triggerKey);
logger.info(">>>>>>>>> Already exist trigger [" + trigger + "] by key [" + triggerKey + "] in Scheduler");
return false;
}
// CronTrigger : TriggerKey + cronExpression
CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(cronExpression);
CronTrigger cronTrigger = TriggerBuilder.newTrigger().withIdentity(triggerKey).withSchedule(cronScheduleBuilder).build();
// JobDetail : jobClass
JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(triggerKeyName, group).build();
if (jobData!=null && jobData.size() > 0) {
JobDataMap jobDataMap = jobDetail.getJobDataMap();
jobDataMap.putAll(jobData); // 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;
}
// reschedule 重置cron
public static boolean rescheduleJob(String triggerKeyName, String cronExpression) throws SchedulerException {
// TriggerKey : name + group
String group = Scheduler.DEFAULT_GROUP;
TriggerKey triggerKey = TriggerKey.triggerKey(triggerKeyName, group);
boolean result = false;
if (scheduler.checkExists(triggerKey)) {
// CronTrigger : TriggerKey + cronExpression
CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(cronExpression);
CronTrigger cronTrigger = TriggerBuilder.newTrigger().withIdentity(triggerKey).withSchedule(cronScheduleBuilder).build();
Date date = scheduler.rescheduleJob(triggerKey, cronTrigger);
result = true;
logger.info(">>>>>>>>>>> resumeJob success, triggerKey:{}, cronExpression:{}, date:{}", triggerKey, cronExpression, date);
} else {
logger.info(">>>>>>>>>>> resumeJob fail, triggerKey:{}, cronExpression:{}", triggerKey, cronExpression);
}
return result;
}
// unscheduleJob 删除
public static boolean removeJob(String triggerKeyName) throws SchedulerException {
// TriggerKey : name + group
String group = Scheduler.DEFAULT_GROUP;
TriggerKey triggerKey = TriggerKey.triggerKey(triggerKeyName, group);
boolean result = false;
if (scheduler.checkExists(triggerKey)) {
result = scheduler.unscheduleJob(triggerKey);
}
logger.info(">>>>>>>>>>> removeJob, triggerKey:{}, result [{}]", triggerKey, result);
return result;
}
// Pause 暂停
public static boolean pauseJob(String triggerKeyName) throws SchedulerException {
// TriggerKey : name + group
String group = Scheduler.DEFAULT_GROUP;
TriggerKey triggerKey = TriggerKey.triggerKey(triggerKeyName, group);
boolean result = false;
if (scheduler.checkExists(triggerKey)) {
scheduler.pauseTrigger(triggerKey);
result = true;
logger.info(">>>>>>>>>>> pauseJob success, triggerKey:{}", triggerKey);
} else {
logger.info(">>>>>>>>>>> pauseJob fail, triggerKey:{}", triggerKey);
}
return result;
}
// resume 重启
public static boolean resumeJob(String triggerKeyName) throws SchedulerException {
// TriggerKey : name + group
String group = Scheduler.DEFAULT_GROUP;
TriggerKey triggerKey = TriggerKey.triggerKey(triggerKeyName, group);
boolean result = false;
if (scheduler.checkExists(triggerKey)) {
scheduler.resumeTrigger(triggerKey);
result = true;
logger.info(">>>>>>>>>>> resumeJob success, triggerKey:{}", triggerKey);
} else {
logger.info(">>>>>>>>>>> resumeJob fail, triggerKey:{}", triggerKey);
}
return result;
}
// run 执行一次
public static boolean triggerJob(String triggerKeyName) throws SchedulerException {
// TriggerKey : name + group
String group = Scheduler.DEFAULT_GROUP;
JobKey jobKey = JobKey.jobKey(triggerKeyName, group);
boolean result = false;
if (scheduler.checkExists(jobKey)) {
scheduler.triggerJob(jobKey);
result = true;
logger.info(">>>>>>>>>>> runJob success, jobKey:{}", jobKey);
} else {
logger.info(">>>>>>>>>>> runJob fail, jobKey:{}", jobKey);
}
return result;
}
}
\ No newline at end of file
package com.xxl.job.service;
/**
* local trigger, only exists in local jvm
* @author xuxueli 2015-12-17 17:29:23
*/
public interface ITriggerService {
public void beat();
}
package com.xxl.job.service.impl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import com.xxl.job.service.ITriggerService;
/**
* local trigger, only exists in local jvm
* @author xuxueli 2015-12-17 17:31:24
*/
@Service("triggerService")
public class TriggerServiceImpl implements ITriggerService {
private static transient Logger logger = LoggerFactory.getLogger(TriggerServiceImpl.class);
public void beat() {
logger.info(">>>>>>>>>>> xxl-job beat success.");
}
}
package com.xxl.job.service.job;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.TimeUnit;
import org.quartz.DisallowConcurrentExecution;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.quartz.QuartzJobBean;
/**
* http job bean
* @author xuxueli 2015-12-17 18:20:34
*/
@DisallowConcurrentExecution // 取消并发执行;线程数要多配置几个,否则不生效;
public class DemoJobBean extends QuartzJobBean {
private static Logger logger = LoggerFactory.getLogger(DemoJobBean.class);
@Override
protected void executeInternal(JobExecutionContext context)
throws JobExecutionException {
String triggerKey = context.getTrigger().getKey().getName();
String triggerGroup = context.getTrigger().getKey().getGroup();
Map<String, Object> jobDataMap = context.getMergedJobDataMap().getWrappedMap();
// jobDataMap 2 params
Map<String, String> params = new HashMap<String, String>();
if (jobDataMap!=null && jobDataMap.size()>0) {
for (Entry<String, Object> item : jobDataMap.entrySet()) {
params.put(item.getKey(), String.valueOf(item.getValue()));
}
}
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
logger.info(">>>>>>>>>>> xxl-job run :jobId:{}, group:{}, jobDataMap:{}",
new Object[]{triggerKey, triggerGroup, jobDataMap});
}
}
\ No newline at end of file
package com.xxl.job.service.job;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.quartz.QuartzJobBean;
/**
* http job bean
* @author xuxueli 2015-12-17 18:20:34
*/
public class DemoJobBeanB extends QuartzJobBean {
private static Logger logger = LoggerFactory.getLogger(DemoJobBeanB.class);
@Override
protected void executeInternal(JobExecutionContext context)
throws JobExecutionException {
String triggerKey = context.getTrigger().getKey().getName();
String triggerGroup = context.getTrigger().getKey().getGroup();
Map<String, Object> jobDataMap = context.getMergedJobDataMap().getWrappedMap();
// jobDataMap 2 params
Map<String, String> params = new HashMap<String, String>();
if (jobDataMap!=null && jobDataMap.size()>0) {
for (Entry<String, Object> item : jobDataMap.entrySet()) {
params.put(item.getKey(), String.valueOf(item.getValue()));
}
}
logger.info(">>>>>>>>>>> xxl-job run :jobId:{}, group:{}, jobDataMap:{}",
new Object[]{triggerKey, triggerGroup, jobDataMap});
}
}
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util.xsd">
<context:annotation-config />
<context:component-scan base-package="com.xxl.job.service" />
<bean id="freemarkerConfig" class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
<property name="templateLoaderPath" value="/WEB-INF/template/" />
<property name="freemarkerSettings">
<bean class="org.springframework.beans.factory.config.PropertiesFactoryBean">
<property name="location" value="classpath:freemarker.properties" />
</bean>
</property>
</bean>
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="fileEncoding" value="utf-8" />
<property name="locations">
<list>
<value>classpath*:jdbc.properties</value>
</list>
</property>
</bean>
</beans>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">
<context:annotation-config />
<context:component-scan base-package="com.xxl.service.impl, com.xxl.dao.impl" />
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
<property name="driverClass" value="${c3p0.driverClass}" />
<property name="jdbcUrl" value="${c3p0.url}" />
<property name="user" value="${c3p0.user}" />
<property name="password" value="${c3p0.password}" />
<property name="initialPoolSize" value="3" />
<property name="minPoolSize" value="2" />
<property name="maxPoolSize" value="10" />
<property name="maxIdleTime" value="60" />
<property name="acquireRetryDelay" value="1000" />
<property name="acquireRetryAttempts" value="10" />
<property name="preferredTestQuery" value="SELECT 1" />
</bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="mapperLocations" value="classpath*:com/xxl/core/model/mapper/*.xml"/>
</bean>
<!-- scope must be "prototype" when junit -->
<bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate" scope="prototype">
<constructor-arg index="0" ref="sqlSessionFactory" />
</bean>
</beans>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util.xsd">
<bean id="quartzScheduler" lazy-init="false" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="autoStartup" value="true" />
<property name="applicationContextSchedulerContextKey" value="applicationContextKey" />
<property name="configLocation" value="classpath:quartz.properties"/>
</bean>
<!-- 协同-调度器 -->
<bean id="dynamicSchedulerUtil" class="com.xxl.job.core.util.DynamicSchedulerUtil">
<!-- (轻易不要变更“调度器名称”, 任务创建时会绑定该“调度器名称”) -->
<property name="scheduler" ref="quartzScheduler"/>
</bean>
</beans>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util.xsd">
<bean id="beatJobDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<property name="targetObject" ref="triggerService" />
<property name="targetMethod" value="beat" />
<property name="concurrent" value="false" />
</bean>
<bean id="beatTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
<property name="jobDetail" ref="beatJobDetail" />
<property name="cronExpression" value="0/10 * * * * ? *" />
</bean>
<!-- 进程-调度器 -->
<bean name="jvmQuertz" lazy-init="false" autowire="no" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="triggers">
<list>
<!-- <ref bean="beatTrigger" /> -->
</list>
</property>
</bean>
</beans>
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true"/>
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="detail*" propagation="SUPPORTS" />
<tx:method name="visit*" propagation="SUPPORTS" />
<tx:method name="get*" propagation="SUPPORTS" />
<tx:method name="find*" propagation="SUPPORTS" />
<tx:method name="check*" propagation="SUPPORTS" />
<tx:method name="list*" propagation="SUPPORTS" />
<tx:method name="*" propagation="REQUIRED" rollback-for="exception" />
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="txoperation" expression="execution(* com.xxl.service.imp.*.*(..))" />
<aop:advisor pointcut-ref="txoperation" advice-ref="txAdvice" />
</aop:config>
</beans>
\ No newline at end of file
template_update_delay=0
default_encoding=UTF-8
output_encoding=UTF-8
locale=zh_CN
number_format=0.##########
date_format=yyyy-MM-dd
time_format=HH:mm:ss
datetime_format=yyyy-MM-dd HH:mm:s
classic_compatible=true
template_exception_handler=ignore
\ No newline at end of file
c3p0.driverClass=com.mysql.jdbc.Driver
c3p0.url=jdbc:mysql://localhost:3306/test?Unicode=true&amp;characterEncoding=UTF-8
c3p0.user=root
c3p0.password=root_pwd
\ No newline at end of file
log4j.rootLogger=info,console,logFile
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=%d - xxl-job-admin - %p [%c] - <%m>%n
log4j.appender.logFile=org.apache.log4j.DailyRollingFileAppender
log4j.appender.logFile.File=${catalina.base}/logs/xxl-job-admin.log
log4j.appender.logFile.layout=org.apache.log4j.PatternLayout
log4j.appender.logFile.layout.ConversionPattern=%d - xxl-job-admin - %p [%c] - <%m>%n
# Default Properties file for use by StdSchedulerFactory
# to create a Quartz Scheduler Instance, if a different
# properties file is not explicitly specified.
#
org.quartz.scheduler.instanceName: DefaultQuartzScheduler
org.quartz.scheduler.rmi.export: false
org.quartz.scheduler.rmi.proxy: false
org.quartz.scheduler.wrapJobExecutionInUserTransaction: false
org.quartz.threadPool.class: org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount: 10
org.quartz.threadPool.threadPriority: 5
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread: true
org.quartz.jobStore.misfireThreshold: 60000
#org.quartz.jobStore.class: org.quartz.simpl.RAMJobStore
# for cluster
org.quartz.jobStore.tablePrefix = XXL_JOB_QRTZ_
org.quartz.scheduler.instanceId: AUTO
org.quartz.jobStore.class: org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.isClustered: true
org.quartz.jobStore.clusterCheckinInterval: 1000
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">
<mvc:annotation-driven />
<context:component-scan base-package="com.xxl.job.controller" />
<mvc:resources mapping="/favicon.ico" location="/favicon.ico" />
<mvc:resources mapping="/static/**" location="/static/" />
<mvc:resources mapping="/**/*.html" location="/" />
<bean id="viewResolver" class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.freemarker.FreeMarkerView" />
<property name="prefix" value="" />
<property name="suffix" value=".ftl" />
<property name="contentType" value="text/html;charset=UTF-8" />
<property name="exposeSpringMacroHelpers" value="true" />
<property name="exposeRequestAttributes" value="true" />
<property name="exposeSessionAttributes" value="true" />
<property name="requestContextAttribute" value="request" />
<property name="cache" value="true" />
<property name="order" value="0" />
</bean>
<!--
// 自定义拦截器,支持SSO登陆拦截
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean class="com.xxl.controller.interceptor.PermissionInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
<bean id="exceptionResolver" class="com.xxl.controller.resolver.WebExceptionResolver" />
-->
</beans>
\ No newline at end of file
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>应用程序异常 (500)</title>
<style type="text/css">
body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }
div.dialog {
width: 80%;
padding: 1em 4em;
margin: 4em auto 0 auto;
border: 1px solid #ccc;
border-right-color: #999;
border-bottom-color: #999;
}
h1 { font-size: 100%; color: #f00; line-height: 1.5em; }
</style>
</head>
</head>
<body>
<div class="dialog">
<h1>应用程序异常</h1>
<p>抱歉!您访问的页面出现异常,请稍后重试或联系管理员。</p>
<p><a href="javascript:showErr();">详 情</a>
<a href="javascript:window.location.href='${request.contextPath}'">返 回</a>
</p>
<div style="display:none;text-align: left;" id="err">${exceptionMsg}</div>
</div>
<script type="text/javascript">
function showErr(){
document.getElementById("err").style.display = "";
}
</script>
</body>
</html>
\ No newline at end of file
<#macro commonStyle>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<!-- Tell the browser to be responsive to screen width -->
<meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport">
<!-- Bootstrap 3.3.5 -->
<link rel="stylesheet" href="${request.contextPath}/static/adminlte/bootstrap/css/bootstrap.min.css">
<!-- Font Awesome -->
<!-- <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.4.0/css/font-awesome.min.css"> -->
<link rel="stylesheet" href="${request.contextPath}/static/plugins/font-awesome-4.3.0/css/font-awesome.min.css">
<!-- Ionicons -->
<!-- <link rel="stylesheet" href="https://code.ionicframework.com/ionicons/2.0.1/css/ionicons.min.css"> -->
<link rel="stylesheet" href="${request.contextPath}/static/plugins/ionicons-2.0.1/css/ionicons.min.css">
<!-- Theme style -->
<link rel="stylesheet" href="${request.contextPath}/static/adminlte/dist/css/AdminLTE.min.css">
<!-- AdminLTE Skins. Choose a skin from the css/skins folder instead of downloading all of them to reduce the load. -->
<link rel="stylesheet" href="${request.contextPath}/static/adminlte/dist/css/skins/_all-skins.min.css">
<!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/html5shiv/3.7.3/html5shiv.min.js"></script>
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
<![endif]-->
<!-- scrollup -->
<link rel="stylesheet" href="${request.contextPath}/static/plugins/scrollup/image.css">
</#macro>
<#macro commonScript>
<!-- jQuery 2.1.4 -->
<script src="${request.contextPath}/static/adminlte/plugins/jQuery/jQuery-2.1.4.min.js"></script>
<!-- Bootstrap 3.3.5 -->
<script src="${request.contextPath}/static/adminlte/bootstrap/js/bootstrap.min.js"></script>
<!-- FastClick -->
<script src="${request.contextPath}/static/adminlte/plugins/fastclick/fastclick.js"></script>
<!-- AdminLTE App -->
<script src="${request.contextPath}/static/adminlte/dist/js/app.min.js"></script>
<!-- scrollup -->
<script src="${request.contextPath}/static/plugins/scrollup/jquery.scrollUp.min.js"></script>
<script src="${request.contextPath}/static/js/common.1.js"></script>
</#macro>
<#macro commonHeader>
<header class="main-header">
<a href="${request.contextPath}/" class="logo">
<span class="logo-mini"><b>X</b>XL</span>
<span class="logo-lg"><b>任务调度</b>中心</span>
</a>
<nav class="navbar navbar-static-top" role="navigation">
<a href="#" class="sidebar-toggle" data-toggle="offcanvas" role="button"><span class="sr-only">切换导航</span></a>
<div class="navbar-custom-menu"></div>
</nav>
</header>
</#macro>
<#macro commonLeft>
<!-- Left side column. contains the logo and sidebar -->
<aside class="main-sidebar">
<!-- sidebar: style can be found in sidebar.less -->
<section class="sidebar">
<!-- sidebar menu: : style can be found in sidebar.less -->
<ul class="sidebar-menu">
<li class="header">常用模块</li>
<li class="nav-click" ><a href="${request.contextPath}//"><i class="fa fa-circle-o text-red"></i> <span>调度中心</span></a></li>
<li class="nav-click" ><a href="${request.contextPath}/help"><i class="fa fa-circle-o text-yellow"></i><span>使用教程</span></a></li>
</ul>
</section>
<!-- /.sidebar -->
</aside>
</#macro>
<#macro commonControl >
<!-- Control Sidebar -->
<aside class="control-sidebar control-sidebar-dark">
<!-- Create the tabs -->
<ul class="nav nav-tabs nav-justified control-sidebar-tabs">
<li class="active"><a href="#control-sidebar-home-tab" data-toggle="tab"><i class="fa fa-home"></i></a></li>
<li><a href="#control-sidebar-settings-tab" data-toggle="tab"><i class="fa fa-gears"></i></a></li>
</ul>
<!-- Tab panes -->
<div class="tab-content">
<!-- Home tab content -->
<div class="tab-pane active" id="control-sidebar-home-tab">
<h3 class="control-sidebar-heading">近期活动</h3>
<ul class="control-sidebar-menu">
<li>
<a href="javascript::;">
<i class="menu-icon fa fa-birthday-cake bg-red"></i>
<div class="menu-info">
<h4 class="control-sidebar-subheading">张三今天过生日</h4>
<p>2015-09-10</p>
</div>
</a>
</li>
<li>
<a href="javascript::;">
<i class="menu-icon fa fa-user bg-yellow"></i>
<div class="menu-info">
<h4 class="control-sidebar-subheading">Frodo 更新了资料</h4>
<p>更新手机号码 +1(800)555-1234</p>
</div>
</a>
</li>
<li>
<a href="javascript::;">
<i class="menu-icon fa fa-envelope-o bg-light-blue"></i>
<div class="menu-info">
<h4 class="control-sidebar-subheading">Nora 加入邮件列表</h4>
<p>nora@example.com</p>
</div>
</a>
</li>
<li>
<a href="javascript::;">
<i class="menu-icon fa fa-file-code-o bg-green"></i>
<div class="menu-info">
<h4 class="control-sidebar-subheading">001号定时作业调度</h4>
<p>5秒前执行</p>
</div>
</a>
</li>
</ul>
<!-- /.control-sidebar-menu -->
</div>
<!-- /.tab-pane -->
<!-- Settings tab content -->
<div class="tab-pane" id="control-sidebar-settings-tab">
<form method="post">
<h3 class="control-sidebar-heading">个人设置</h3>
<div class="form-group">
<label class="control-sidebar-subheading"> 左侧菜单自适应
<input type="checkbox" class="pull-right" checked>
</label>
<p>左侧菜单栏样式自适应</p>
</div>
<!-- /.form-group -->
</form>
</div>
<!-- /.tab-pane -->
</div>
</aside>
<!-- /.control-sidebar -->
<!-- Add the sidebar's background. This div must be placed immediately after the control sidebar -->
<div class="control-sidebar-bg"></div>
</#macro>
<#macro commonFooter >
<footer class="main-footer">
<div class="pull-right hidden-xs">
<b>Version</b> 1.0
</div>
<strong>Copyright &copy; 2015-2015 &nbsp;
<a href="https://github.com/xuxueli/xxl-job" target="_blank" >github</a>&nbsp;
<a href="http://www.cnblogs.com/xuxueli/p/5021979.html" target="_blank" >cnblog</a>.
</strong> All rights reserved.
</footer>
</#macro>
<#macro comAlert >
<!-- ComAlert.模态框Modal -->
<div class="modal fade" id="ComAlert" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<!-- <div class="modal-header"><h4 class="modal-title"><strong>提示:</strong></h4></div> -->
<div class="modal-body"><div class="alert alert-success"></div></div>
<div class="modal-footer">
<div class="text-center" >
<button type="button" class="btn btn-default ok" data-dismiss="modal" >确认</button>
</div>
</div>
</div>
</div>
</div>
<!-- ComConfirm.模态框Modal -->
<div class="modal fade" id="ComConfirm" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-body"><div class="alert alert-success"></div></div>
<div class="modal-footer">
<div class="text-center" >
<button type="button" class="btn btn-primary ok" data-dismiss="modal" >确认</button>
<button type="button" class="btn btn-default cancel" data-dismiss="modal" >取消</button>
</div>
</div>
</div>
</div>
</div>
<script>
// 通用提示
var ComAlert = {
show:function(type, msg, callback){
// 弹框初始
if (type == 1) {
$('#ComAlert .alert').attr('class', 'alert alert-success');
} else {
$('#ComAlert .alert').attr('class', 'alert alert-warning');
}
$('#ComAlert .alert').html(msg);
$('#ComAlert').modal('show');
$('#ComAlert .ok').click(function(){
$('#ComAlert').modal('hide');
if(typeof callback == 'function') {
callback();
}
});
// $("#ComAlert").on('hide.bs.modal', function () { }); // 监听关闭
}
};
// 通用确认弹框
var ComConfirm = {
show:function(msg, callback){
// 弹框初始
$('#ComConfirm .alert').attr('class', 'alert alert-warning');
$('#ComConfirm .alert').html(msg);
$('#ComConfirm').modal('show');
$('#ComConfirm .ok').unbind("click"); // 解绑陈旧事件
$('#ComConfirm .ok').click(function(){
$('#ComConfirm').modal('hide');
if(typeof callback == 'function') {
callback();
return;
}
});
$('#ComConfirm .cancel').click(function(){
$('#ComConfirm').modal('hide');
return;
});
}
};
</script>
</#macro>
\ No newline at end of file
<!DOCTYPE html>
<html>
<head>
<title>调度中心</title>
<#import "/common/common.macro.ftl" as netCommon>
<@netCommon.commonStyle />
</head>
<body class="hold-transition skin-blue sidebar-mini">
<div class="wrapper">
<!-- header -->
<@netCommon.commonHeader />
<!-- left -->
<@netCommon.commonLeft />
<!-- Content Wrapper. Contains page content -->
<div class="content-wrapper">
<!-- Content Header (Page header) -->
<section class="content-header">
<h1>使用教程<small>调度管理平台</small></h1>
<ol class="breadcrumb">
<li><a><i class="fa fa-dashboard"></i>调度中心</a></li>
<li class="active">使用教程</li>
</ol>
</section>
<!-- Main content -->
<section class="content">
<div class="callout callout-info">
<h4>简介:XXL_JOB</h4>
<p>基于quartz封装实现的的集群任务调度管理平台.</p>
<p></p>
</div>
<div class="callout callout-default">
<h4>特点:</h4>
<p>1、简单:支持通过Web页面对任务进行CRUD操作,操作简单,一分钟上手.</p>
<p>2、动态:支持动态修改任务状态,动态暂停/恢复任务,即时生效.</p>
<p>3、集群:任务信息持久化到mysql中,支持Job服务器集群(高可用),一个任务只会在其中一台服务器上执行.</p>
</div>
<div class="callout callout-default">
<h4>分层模型:</h4>
<p>1、基础:基于quartz封装底层调度层,通过CORN自定义任务执行周期,最终执行自定义JobBean的execute方法,如需多个任务,需要开发多个JobBean实现.</p>
<p>2、分层:上述基础调度模型存在一定局限,调度层和任务层耦合,当新任务上线势必影响任务的正常调度,因此规划将调度系统分层为:调度层 + 任务层 + 通讯层.</p>
<p>
<div class="row">
<div class="col-xs-offset-1 col-xs-11">
<p>》调度模块:维护任务的调度信息,负责定时/周期性的发出调度请求.</p>
<p>》任务模块:具体的任务逻辑,负责接收调度模块的调度请求,执行任务逻辑.</p>
<p>》通讯模块:负责调度模块和任务模块之间的通讯.</p>
<p>(总而言之,一条完整任务由 “调度信息” 和 “任务信息” 组成.)</p>
</div>
</div>
</p>
</div>
<div class="callout callout-default">
<h4>调度属性解析 : 发出HTTP调度请求</h4>
<p>1、调度Key【必填】:调度信息的全局唯一标识.</p>
<p>2、调度Corn【必填】:调度执行的时间表达式.</p>
<p>3、调度描述【必填】:调度的简述.</p>
<p>4、调度URL【必填】:调度执行时发出HTTP请求的目标URL地址.</p>
<p>5、+args【选填】:调度执行时发出HTTP请求的附带的POST参数.</p>
</div>
</section>
<!-- /.content -->
</div>
<!-- /.content-wrapper -->
<!-- footer -->
<@netCommon.commonFooter />
<!-- control -->
<@netCommon.commonControl />
</div>
<@netCommon.commonScript />
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<title>调度中心</title>
<#import "/common/common.macro.ftl" as netCommon>
<@netCommon.commonStyle />
<!-- DataTables -->
<link rel="stylesheet" href="${request.contextPath}/static/adminlte/plugins/datatables/dataTables.bootstrap.css">
</head>
<body class="hold-transition skin-blue sidebar-mini">
<div class="wrapper">
<!-- header -->
<@netCommon.commonHeader />
<!-- left -->
<@netCommon.commonLeft />
<!-- Content Wrapper. Contains page content -->
<div class="content-wrapper">
<!-- Content Header (Page header) -->
<section class="content-header">
<h1>调度中心<small>调度管理</small></h1>
<ol class="breadcrumb">
<li><a><i class="fa fa-dashboard"></i>调度中心</a></li>
<li class="active">调度管理</li>
</ol>
</section>
<!-- Main content -->
<section class="content">
<div class="row">
<div class="col-xs-12">
<div class="box">
<div class="box-header">
<h3 class="box-title">调度列表</h3>
<button class="btn btn-info btn-xs add" type="button">+新增任务</button>
</div>
<div class="box-body">
<table id="job_list" class="table table-bordered table-striped">
<thead>
<tr>
<th>调度key</th>
<th>cron</th>
<!--<th>类路径</th>-->
<th>参数</th>
<th>状态</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<#if jobList?exists && jobList?size gt 0>
<#list jobList as item>
<tr>
<td>${item['TriggerKey'].name}</td>
<td>${item['Trigger'].cronExpression}</td>
<!--<td>${item['JobDetail'].jobClass}</td>-->
<td>
<#assign jobDataMap = item['JobDetail'].jobDataMap />
<#if jobDataMap?exists && jobDataMap?keys?size gt 0>
<#list jobDataMap?keys as key>
${key} = ${jobDataMap[key]} <br>
</#list>
</#if>
</td>
<td state="${item['TriggerState']}" >
<#if item['TriggerState'] == 'NORMAL'>
<button class="btn btn-block btn-success" type="button">运行ing</button>
<#elseif item['TriggerState'] == 'PAUSED'>
<button class="btn btn-block btn-warning" type="button">暂停ing</button>
<#else>
<button class="btn btn-block" type="button">${item['TriggerState']}</button>
</#if>
</td>
<td>
<p name="${item['TriggerKey'].name}" group="${item['TriggerKey'].group}"
cronExpression="${item['Trigger'].cronExpression}" jobClassName="${item['JobDetail'].jobClass}" jobDesc="${job_desc?if_exists}" >
<#if item['TriggerState'] == 'NORMAL'>
<button class="btn btn-info btn-xs job_operate" type="job_pause" type="button">暂停</button>
<#elseif item['TriggerState'] == 'PAUSED'>
<button class="btn btn-info btn-xs job_operate" type="job_resume" type="button">恢复</button>
</#if>
<button class="btn btn-info btn-xs job_operate" type="job_trigger" type="button">执行一次</button>
<button class="btn btn-danger btn-xs job_operate" type="job_del" type="button">删除</button>
<button class="btn btn-info btn-xs update" type="button">更新corn</button>
</p>
</td>
</tr>
</#list>
</#if>
</tbody>
<tfoot>
<tr>
<th>调度key</th>
<th>cron</th>
<!--<th>类路径</th>-->
<th>参数</th>
<th>状态</th>
<th>操作</th>
</tr>
</tfoot>
</table>
</div>
</div>
</div>
</div>
</section>
</div>
<!-- footer -->
<@netCommon.commonFooter />
<!-- control -->
<@netCommon.commonControl />
</div>
<!-- job新增.模态框 -->
<div class="modal fade" id="addModal" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title" >新增调度信息</h4>
</div>
<div class="modal-body">
<form class="form-horizontal form" role="form" >
<div class="form-group">
<label for="firstname" class="col-sm-3 control-label">任务Key</label>
<div class="col-sm-9"><input type="text" class="form-control" name="triggerKeyName" placeholder="请输入任务Key" minlength="4" maxlength="100" ></div>
</div>
<div class="form-group">
<label for="lastname" class="col-sm-3 control-label">任务Corn</label>
<div class="col-sm-9"><input type="text" class="form-control" name="cronExpression" placeholder="请输入任务Corn[允许修改]" maxlength="100" ></div>
</div>
<div class="form-group">
<label for="lastname" class="col-sm-3 control-label">任务描述</label>
<div class="col-sm-9"><input type="text" class="form-control" name="job_desc" placeholder="请输入任务描述[不支持修改]" maxlength="200" ></div>
</div>
<div class="form-group">
<label for="lastname" class="col-sm-3 control-label">任务JobBean</label>
<div class="col-sm-9">
<select name="job_bean" >
<#if jobBeanMap?exists && jobBeanMap?size gt 0>
<#list jobBeanMap?keys as key>
<option value ="${key}" <#if key_index == 0>selected="selected"</#if> >${jobBeanMap[key]}[${key}]</option>
</#list>
</#if>
</select>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-3 col-sm-9">
<button type="submit" class="btn btn-primary" >保存</button>
<button type="button" class="btn btn-default" data-dismiss="modal">取消</button>
<button type="button" class="btn btn-info pull-right addParam">+ arg</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
<!-- 更新.模态框 -->
<div class="modal fade" id="updateModal" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title" >更新corn</h4>
</div>
<div class="modal-body">
<form class="form-horizontal form" role="form" >
<div class="form-group">
<label for="firstname" class="col-sm-2 control-label">任务Key</label>
<div class="col-sm-10"><input type="text" class="form-control" name="triggerKeyName" placeholder="请输入任务Key" minlength="4" maxlength="100" readonly ></div>
</div>
<div class="form-group">
<label for="lastname" class="col-sm-2 control-label">任务Corn</label>
<div class="col-sm-10"><input type="text" class="form-control" name="cronExpression" placeholder="请输入任务Corn" maxlength="100" ></div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button type="submit" class="btn btn-primary" >保存</button>
<button type="button" class="btn btn-default" data-dismiss="modal">取消</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
<@netCommon.commonScript />
<@netCommon.comAlert />
<!-- DataTables -->
<script src="${request.contextPath}/static/adminlte/plugins/datatables/jquery.dataTables.min.js"></script>
<script src="${request.contextPath}/static/adminlte/plugins/datatables/dataTables.bootstrap.min.js"></script>
<script src="${request.contextPath}/static/plugins/jquery/jquery.validate.min.js"></script>
<script>var base_url = '${request.contextPath}';</script>
<script src="${request.contextPath}/static/js/job.index.1.js"></script>
</body>
</html>
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
id="WebApp_ID" version="2.5">
<display-name>xxl-job-admin</display-name>
<context-param>
<param-name>webAppRootKey</param-name>
<param-value>xxl-job-admin</param-value>
</context-param>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:applicationcontext-*.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
</listener>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:springmvc-context.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
</welcome-file-list>
<error-page>
<error-code>404</error-code>
<location>/500.html</location>
</error-page>
<error-page>
<error-code>500</error-code>
<location>/500.html</location>
</error-page>
</web-app>
\ No newline at end of file
/*
* Author: Abdullah A Almsaeed
* Date: 4 Jan 2014
* Description:
* This is a demo file used only for the main dashboard (index.html)
**/
$(function () {
"use strict";
//Make the dashboard widgets sortable Using jquery UI
$(".connectedSortable").sortable({
placeholder: "sort-highlight",
connectWith: ".connectedSortable",
handle: ".box-header, .nav-tabs",
forcePlaceholderSize: true,
zIndex: 999999
});
$(".connectedSortable .box-header, .connectedSortable .nav-tabs-custom").css("cursor", "move");
//jQuery UI sortable for the todo list
$(".todo-list").sortable({
placeholder: "sort-highlight",
handle: ".handle",
forcePlaceholderSize: true,
zIndex: 999999
});
//bootstrap WYSIHTML5 - text editor
$(".textarea").wysihtml5();
$('.daterange').daterangepicker({
ranges: {
'Today': [moment(), moment()],
'Yesterday': [moment().subtract(1, 'days'), moment().subtract(1, 'days')],
'Last 7 Days': [moment().subtract(6, 'days'), moment()],
'Last 30 Days': [moment().subtract(29, 'days'), moment()],
'This Month': [moment().startOf('month'), moment().endOf('month')],
'Last Month': [moment().subtract(1, 'month').startOf('month'), moment().subtract(1, 'month').endOf('month')]
},
startDate: moment().subtract(29, 'days'),
endDate: moment()
}, function (start, end) {
window.alert("You chose: " + start.format('MMMM D, YYYY') + ' - ' + end.format('MMMM D, YYYY'));
});
/* jQueryKnob */
$(".knob").knob();
//jvectormap data
var visitorsData = {
"US": 398, //USA
"SA": 400, //Saudi Arabia
"CA": 1000, //Canada
"DE": 500, //Germany
"FR": 760, //France
"CN": 300, //China
"AU": 700, //Australia
"BR": 600, //Brazil
"IN": 800, //India
"GB": 320, //Great Britain
"RU": 3000 //Russia
};
//World map by jvectormap
$('#world-map').vectorMap({
map: 'world_mill_en',
backgroundColor: "transparent",
regionStyle: {
initial: {
fill: '#e4e4e4',
"fill-opacity": 1,
stroke: 'none',
"stroke-width": 0,
"stroke-opacity": 1
}
},
series: {
regions: [{
values: visitorsData,
scale: ["#92c1dc", "#ebf4f9"],
normalizeFunction: 'polynomial'
}]
},
onRegionLabelShow: function (e, el, code) {
if (typeof visitorsData[code] != "undefined")
el.html(el.html() + ': ' + visitorsData[code] + ' new visitors');
}
});
//Sparkline charts
var myvalues = [1000, 1200, 920, 927, 931, 1027, 819, 930, 1021];
$('#sparkline-1').sparkline(myvalues, {
type: 'line',
lineColor: '#92c1dc',
fillColor: "#ebf4f9",
height: '50',
width: '80'
});
myvalues = [515, 519, 520, 522, 652, 810, 370, 627, 319, 630, 921];
$('#sparkline-2').sparkline(myvalues, {
type: 'line',
lineColor: '#92c1dc',
fillColor: "#ebf4f9",
height: '50',
width: '80'
});
myvalues = [15, 19, 20, 22, 33, 27, 31, 27, 19, 30, 21];
$('#sparkline-3').sparkline(myvalues, {
type: 'line',
lineColor: '#92c1dc',
fillColor: "#ebf4f9",
height: '50',
width: '80'
});
//The Calender
$("#calendar").datepicker();
//SLIMSCROLL FOR CHAT WIDGET
$('#chat-box').slimScroll({
height: '250px'
});
/* Morris.js Charts */
// Sales chart
var area = new Morris.Area({
element: 'revenue-chart',
resize: true,
data: [
{y: '2011 Q1', item1: 2666, item2: 2666},
{y: '2011 Q2', item1: 2778, item2: 2294},
{y: '2011 Q3', item1: 4912, item2: 1969},
{y: '2011 Q4', item1: 3767, item2: 3597},
{y: '2012 Q1', item1: 6810, item2: 1914},
{y: '2012 Q2', item1: 5670, item2: 4293},
{y: '2012 Q3', item1: 4820, item2: 3795},
{y: '2012 Q4', item1: 15073, item2: 5967},
{y: '2013 Q1', item1: 10687, item2: 4460},
{y: '2013 Q2', item1: 8432, item2: 5713}
],
xkey: 'y',
ykeys: ['item1', 'item2'],
labels: ['Item 1', 'Item 2'],
lineColors: ['#a0d0e0', '#3c8dbc'],
hideHover: 'auto'
});
var line = new Morris.Line({
element: 'line-chart',
resize: true,
data: [
{y: '2011 Q1', item1: 2666},
{y: '2011 Q2', item1: 2778},
{y: '2011 Q3', item1: 4912},
{y: '2011 Q4', item1: 3767},
{y: '2012 Q1', item1: 6810},
{y: '2012 Q2', item1: 5670},
{y: '2012 Q3', item1: 4820},
{y: '2012 Q4', item1: 15073},
{y: '2013 Q1', item1: 10687},
{y: '2013 Q2', item1: 8432}
],
xkey: 'y',
ykeys: ['item1'],
labels: ['Item 1'],
lineColors: ['#efefef'],
lineWidth: 2,
hideHover: 'auto',
gridTextColor: "#fff",
gridStrokeWidth: 0.4,
pointSize: 4,
pointStrokeColors: ["#efefef"],
gridLineColor: "#efefef",
gridTextFamily: "Open Sans",
gridTextSize: 10
});
//Donut Chart
var donut = new Morris.Donut({
element: 'sales-chart',
resize: true,
colors: ["#3c8dbc", "#f56954", "#00a65a"],
data: [
{label: "Download Sales", value: 12},
{label: "In-Store Sales", value: 30},
{label: "Mail-Order Sales", value: 20}
],
hideHover: 'auto'
});
//Fix for charts under tabs
$('.box ul.nav a').on('shown.bs.tab', function () {
area.redraw();
donut.redraw();
line.redraw();
});
/* The todo list plugin */
$(".todo-list").todolist({
onCheck: function (ele) {
window.console.log("The element has been checked");
return ele;
},
onUncheck: function (ele) {
window.console.log("The element has been unchecked");
return ele;
}
});
});
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册