提交 71e12272 编写于 作者: X xueli.xue

任务地址支持配置多个,进行failover

上级 e58f77dd
......@@ -150,17 +150,19 @@ CREATE TABLE `xxl_job_qrtz_trigger_info` (
`job_cron` varchar(128) NOT NULL COMMENT '任务执行CORN',
`job_desc` varchar(255) NOT NULL,
`job_class` varchar(255) NOT NULL COMMENT '任务执行JobBean',
`job_data` varchar(512) DEFAULT NULL COMMENT '任务执行数据',
`add_time` datetime DEFAULT NULL,
`update_time` datetime DEFAULT NULL,
`author` varchar(64) DEFAULT NULL COMMENT '作者',
`alarm_email` varchar(255) DEFAULT NULL COMMENT '报警邮件',
`alarm_threshold` int(11) DEFAULT NULL COMMENT '报警阀值(连续失败次数)',
`executor_address` varchar(255) DEFAULT NULL COMMENT '执行器地址,有多个则逗号分隔',
`executor_handler` varchar(255) DEFAULT NULL COMMENT '执行器任务handler',
`executor_param` varchar(255) DEFAULT NULL COMMENT '执行器任务参数',
`glue_switch` int(11) DEFAULT '0' COMMENT 'GLUE模式开关:0-否,1-是',
`glue_source` text COMMENT 'GLUE源代码',
`glue_remark` varchar(128) DEFAULT NULL COMMENT 'GLUE备注',
PRIMARY KEY (`id`)
);
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
CREATE TABLE `xxl_job_qrtz_trigger_log` (
`id` int(11) NOT NULL AUTO_INCREMENT,
......@@ -169,7 +171,9 @@ CREATE TABLE `xxl_job_qrtz_trigger_log` (
`job_cron` varchar(128) NOT NULL COMMENT '任务执行CORN表达式',
`job_desc` varchar(255) NOT NULL,
`job_class` varchar(255) NOT NULL COMMENT '任务执行JobBean',
`job_data` varchar(512) DEFAULT NULL COMMENT '任务执行数据',
`executor_address` varchar(255) DEFAULT NULL COMMENT '执行器地址,本次执行的地址',
`executor_handler` varchar(255) DEFAULT NULL COMMENT '执行器任务handler',
`executor_param` varchar(255) DEFAULT NULL COMMENT 'executor_param',
`trigger_time` datetime DEFAULT NULL COMMENT '调度-时间',
`trigger_status` varchar(255) DEFAULT NULL COMMENT '调度-结果',
`trigger_msg` varchar(2048) DEFAULT NULL COMMENT '调度-日志',
......@@ -177,7 +181,7 @@ CREATE TABLE `xxl_job_qrtz_trigger_log` (
`handle_status` varchar(255) DEFAULT NULL COMMENT '执行-状态',
`handle_msg` varchar(2048) DEFAULT NULL COMMENT '执行-日志',
PRIMARY KEY (`id`)
);
) ENGINE=InnoDB AUTO_INCREMENT=33 DEFAULT CHARSET=utf8;
CREATE TABLE `xxl_job_qrtz_trigger_logglue` (
`id` int(11) NOT NULL AUTO_INCREMENT,
......@@ -188,7 +192,8 @@ CREATE TABLE `xxl_job_qrtz_trigger_logglue` (
`add_time` timestamp NULL DEFAULT NULL,
`update_time` timestamp NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ;
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;
commit;
......
package com.xxl.job.admin.controller;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.annotation.Resource;
import org.apache.commons.lang.StringUtils;
import org.quartz.CronExpression;
import org.quartz.SchedulerException;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
......@@ -16,15 +11,8 @@ import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import com.xxl.job.admin.core.constant.Constants.JobGroupEnum;
import com.xxl.job.admin.core.jobbean.RemoteHttpJobBean;
import com.xxl.job.admin.core.model.ReturnT;
import com.xxl.job.admin.core.model.XxlJobInfo;
import com.xxl.job.admin.core.util.DynamicSchedulerUtil;
import com.xxl.job.admin.dao.IXxlJobInfoDao;
import com.xxl.job.admin.dao.IXxlJobLogDao;
import com.xxl.job.admin.dao.IXxlJobLogGlueDao;
import com.xxl.job.core.handler.HandlerRepository;
import com.xxl.job.core.util.JacksonUtil;
import com.xxl.job.admin.service.IXxlJobService;
/**
* index controller
......@@ -35,11 +23,7 @@ import com.xxl.job.core.util.JacksonUtil;
public class JobInfoController {
@Resource
private IXxlJobInfoDao xxlJobInfoDao;
@Resource
public IXxlJobLogDao xxlJobLogDao;
@Resource
private IXxlJobLogGlueDao xxlJobLogGlueDao;
private IXxlJobService xxlJobService;
@RequestMapping
public String index(Model model) {
......@@ -53,219 +37,52 @@ public class JobInfoController {
@RequestParam(required = false, defaultValue = "10") int length,
String jobGroup, String jobName, String filterTime) {
// page list
List<XxlJobInfo> list = xxlJobInfoDao.pageList(start, length, jobGroup, jobName);
int list_count = xxlJobInfoDao.pageListCount(start, length, jobGroup, jobName);
// fill job info
if (list!=null && list.size()>0) {
for (XxlJobInfo jobInfo : list) {
DynamicSchedulerUtil.fillJobInfo(jobInfo);
}
}
// package result
Map<String, Object> maps = new HashMap<String, Object>();
maps.put("recordsTotal", list_count); // 总记录数
maps.put("recordsFiltered", list_count); // 过滤后的总记录数
maps.put("data", list); // 分页列表
return maps;
return xxlJobService.pageList(start, length, jobGroup, jobName, filterTime);
}
@RequestMapping("/add")
@ResponseBody
public ReturnT<String> add(String jobGroup, String jobName, String jobCron, String jobDesc,
String handler_address, String handler_name, String handler_params,
String executorAddress, String executorHandler, String executorParam,
String author, String alarmEmail, int alarmThreshold,
int glueSwitch, String glueSource, String glueRemark) {
// valid
if (JobGroupEnum.match(jobGroup) == null) {
return new ReturnT<String>(500, "请选择“任务组”");
}
if (StringUtils.isBlank(jobName)) {
return new ReturnT<String>(500, "请输入“任务名”");
}
if (!CronExpression.isValidExpression(jobCron)) {
return new ReturnT<String>(500, "“corn”不合法");
}
if (StringUtils.isBlank(jobDesc)) {
return new ReturnT<String>(500, "请输入“任务描述”");
}
if (StringUtils.isBlank(handler_address)) {
return new ReturnT<String>(500, "请输入“执行器地址”");
}
if (glueSwitch==0 && StringUtils.isBlank(handler_name)) {
return new ReturnT<String>(500, "请输入“jobHandler”");
}
if (StringUtils.isBlank(author)) {
return new ReturnT<String>(500, "请输入“负责人”");
}
if (StringUtils.isBlank(alarmEmail)) {
return new ReturnT<String>(500, "请输入“报警邮件”");
}
if (alarmThreshold < 0) {
alarmThreshold = 0;
}
try {
if (DynamicSchedulerUtil.checkExists(jobName, jobGroup)) {
return new ReturnT<String>(500, "此任务已存在,请更换任务组或任务名");
}
} catch (SchedulerException e1) {
e1.printStackTrace();
return new ReturnT<String>(500, "此任务已存在,请更换任务组或任务名");
}
// parse jobDataMap
HashMap<String, String> jobDataMap = new HashMap<String, String>();
jobDataMap.put(HandlerRepository.HANDLER_ADDRESS, handler_address);
jobDataMap.put(HandlerRepository.HANDLER_NAME, handler_name);
jobDataMap.put(HandlerRepository.HANDLER_PARAMS, handler_params);
// Backup to the database
XxlJobInfo jobInfo = new XxlJobInfo();
jobInfo.setJobGroup(jobGroup);
jobInfo.setJobName(jobName);
jobInfo.setJobCron(jobCron);
jobInfo.setJobDesc(jobDesc);
jobInfo.setJobClass(RemoteHttpJobBean.class.getName());
jobInfo.setJobData(JacksonUtil.writeValueAsString(jobDataMap));
jobInfo.setAuthor(author);
jobInfo.setAlarmEmail(alarmEmail);
jobInfo.setAlarmThreshold(alarmThreshold);
jobInfo.setGlueSwitch(glueSwitch);
jobInfo.setGlueSource(glueSource);
jobInfo.setGlueRemark(glueRemark);
xxlJobInfoDao.save(jobInfo);
try {
// add job 2 quartz
boolean result = DynamicSchedulerUtil.addJob(jobInfo);
if (result) {
return ReturnT.SUCCESS;
} else {
xxlJobInfoDao.delete(jobGroup, jobName);
return new ReturnT<String>(500, "新增任务失败");
}
} catch (SchedulerException e) {
e.printStackTrace();
}
return ReturnT.FAIL;
return xxlJobService.add(jobGroup, jobName, jobCron, jobDesc, executorAddress, executorHandler, executorParam,
author, alarmEmail, alarmThreshold, glueSwitch, glueSource, glueRemark);
}
@RequestMapping("/reschedule")
@ResponseBody
public ReturnT<String> reschedule(String jobGroup, String jobName, String jobCron, String jobDesc,
String handler_address, String handler_name, String handler_params,
String executorAddress, String executorHandler, String executorParam,
String author, String alarmEmail, int alarmThreshold, int glueSwitch) {
// valid
if (JobGroupEnum.match(jobGroup) == null) {
return new ReturnT<String>(500, "请选择“任务组”");
}
if (StringUtils.isBlank(jobName)) {
return new ReturnT<String>(500, "请输入“任务名”");
}
if (!CronExpression.isValidExpression(jobCron)) {
return new ReturnT<String>(500, "“corn”不合法");
}
if (StringUtils.isBlank(jobDesc)) {
return new ReturnT<String>(500, "请输入“任务描述”");
}
if (StringUtils.isBlank(handler_address)) {
return new ReturnT<String>(500, "请输入“执行器地址”");
}
if (glueSwitch==0 && StringUtils.isBlank(handler_name)) {
return new ReturnT<String>(500, "请输入“jobHandler”");
}
if (StringUtils.isBlank(author)) {
return new ReturnT<String>(500, "请输入“负责人”");
}
if (StringUtils.isBlank(alarmEmail)) {
return new ReturnT<String>(500, "请输入“报警邮件”");
}
if (alarmThreshold < 0) {
alarmThreshold = 0;
}
// parse jobDataMap
HashMap<String, String> jobDataMap = new HashMap<String, String>();
jobDataMap.put(HandlerRepository.HANDLER_ADDRESS, handler_address);
jobDataMap.put(HandlerRepository.HANDLER_NAME, handler_name);
jobDataMap.put(HandlerRepository.HANDLER_PARAMS, handler_params);
XxlJobInfo jobInfo = xxlJobInfoDao.load(jobGroup, jobName);
jobInfo.setJobCron(jobCron);
jobInfo.setJobDesc(jobDesc);
jobInfo.setJobData(JacksonUtil.writeValueAsString(jobDataMap));
jobInfo.setAuthor(author);
jobInfo.setAlarmEmail(alarmEmail);
jobInfo.setAlarmThreshold(alarmThreshold);
jobInfo.setGlueSwitch(glueSwitch);
try {
// fresh quartz
DynamicSchedulerUtil.rescheduleJob(jobInfo);
// fresh db
xxlJobInfoDao.update(jobInfo);
return ReturnT.SUCCESS;
} catch (SchedulerException e) {
e.printStackTrace();
}
return ReturnT.FAIL;
return xxlJobService.reschedule(jobGroup, jobName, jobCron, jobDesc, executorAddress, executorHandler, executorParam, author,
alarmEmail, alarmThreshold, glueSwitch);
}
@RequestMapping("/remove")
@ResponseBody
public ReturnT<String> remove(String jobGroup, String jobName) {
try {
DynamicSchedulerUtil.removeJob(jobName, jobGroup);
xxlJobInfoDao.delete(jobGroup, jobName);
xxlJobLogDao.delete(jobGroup, jobName);
xxlJobLogGlueDao.delete(jobGroup, jobName);
return ReturnT.SUCCESS;
} catch (SchedulerException e) {
e.printStackTrace();
}
return ReturnT.FAIL;
return xxlJobService.remove(jobGroup, jobName);
}
@RequestMapping("/pause")
@ResponseBody
public ReturnT<String> pause(String jobGroup, String jobName) {
try {
DynamicSchedulerUtil.pauseJob(jobName, jobGroup); // jobStatus do not store
return ReturnT.SUCCESS;
} catch (SchedulerException e) {
e.printStackTrace();
return ReturnT.FAIL;
}
return xxlJobService.pause(jobGroup, jobName);
}
@RequestMapping("/resume")
@ResponseBody
public ReturnT<String> resume(String jobGroup, String jobName) {
try {
DynamicSchedulerUtil.resumeJob(jobName, jobGroup);
return ReturnT.SUCCESS;
} catch (SchedulerException e) {
e.printStackTrace();
return ReturnT.FAIL;
}
return xxlJobService.resume(jobGroup, jobName);
}
@RequestMapping("/trigger")
@ResponseBody
public ReturnT<String> triggerJob(String jobGroup, String jobName) {
try {
DynamicSchedulerUtil.triggerJob(jobName, jobGroup);
return ReturnT.SUCCESS;
} catch (SchedulerException e) {
e.printStackTrace();
return ReturnT.FAIL;
}
return xxlJobService.triggerJob(jobGroup, jobName);
}
}
......@@ -20,10 +20,10 @@ import com.xxl.job.admin.core.constant.Constants.JobGroupEnum;
import com.xxl.job.admin.core.model.ReturnT;
import com.xxl.job.admin.core.model.XxlJobLog;
import com.xxl.job.admin.dao.IXxlJobLogDao;
import com.xxl.job.core.handler.HandlerRepository;
import com.xxl.job.core.handler.HandlerRepository.ActionEnum;
import com.xxl.job.core.handler.HandlerRepository.HandlerParamEnum;
import com.xxl.job.core.util.HttpUtil;
import com.xxl.job.core.util.HttpUtil.RemoteCallBack;
import com.xxl.job.core.util.JacksonUtil;
/**
* index controller
......@@ -41,7 +41,7 @@ public class JobLogController {
model.addAttribute("jobGroup", jobGroup);
model.addAttribute("jobName", jobName);
model.addAttribute("JobGroupList", JobGroupEnum.values());
return "joblog/index";
return "joblog/joblog.index";
}
@RequestMapping("/pageList")
......@@ -101,21 +101,18 @@ public class JobLogController {
if (log == null) {
return new ReturnT<String>(500, "参数异常");
}
// server address
@SuppressWarnings("unchecked")
Map<String, String> jobDataMap = JacksonUtil.readValue(log.getJobData(), Map.class);
String handler_address = jobDataMap.get(HandlerRepository.HANDLER_ADDRESS);
if (!handler_address.startsWith("http")){
handler_address = "http://" + handler_address + "/";
if (!RemoteCallBack.SUCCESS.equals(log.getTriggerStatus())) {
return new ReturnT<String>(500, "调度失败,无法查看执行日志");
}
// trigger id, trigger time
Map<String, String> reqMap = new HashMap<String, String>();
reqMap.put(HandlerRepository.NAMESPACE, HandlerRepository.NameSpaceEnum.LOG.name());
reqMap.put(HandlerRepository.TRIGGER_LOG_ID, String.valueOf(id));
reqMap.put(HandlerRepository.TRIGGER_TIMESTAMP, String.valueOf(log.getTriggerTime().getTime()));
reqMap.put(HandlerParamEnum.TIMESTAMP.name(), String.valueOf(System.currentTimeMillis()));
reqMap.put(HandlerParamEnum.ACTION.name(), ActionEnum.LOG.name());
reqMap.put(HandlerParamEnum.LOG_ID.name(), String.valueOf(id));
reqMap.put(HandlerParamEnum.LOG_DATE.name(), String.valueOf(log.getTriggerTime().getTime()));
RemoteCallBack callBack = HttpUtil.post(handler_address, reqMap);
RemoteCallBack callBack = HttpUtil.post(HttpUtil.addressToUrl(log.getExecutorAddress()), reqMap);
if (HttpUtil.RemoteCallBack.SUCCESS.equals(callBack.getStatus())) {
return new ReturnT<String>(callBack.getMsg());
} else {
......@@ -138,23 +135,19 @@ public class JobLogController {
if (log == null) {
return new ReturnT<String>(500, "参数异常");
}
// server address
@SuppressWarnings("unchecked")
Map<String, String> jobDataMap = JacksonUtil.readValue(log.getJobData(), Map.class);
String handler_address = jobDataMap.get(HandlerRepository.HANDLER_ADDRESS);
if (!handler_address.startsWith("http")){
handler_address = "http://" + handler_address + "/";
if (!RemoteCallBack.SUCCESS.equals(log.getTriggerStatus())) {
return new ReturnT<String>(500, "调度失败,无法终止日志");
}
String handler_name = jobDataMap.get(HandlerRepository.HANDLER_NAME);
// trigger id, trigger time
// request
Map<String, String> reqMap = new HashMap<String, String>();
reqMap.put(HandlerRepository.NAMESPACE, HandlerRepository.NameSpaceEnum.KILL.name());
reqMap.put(HandlerRepository.HANDLER_NAME, handler_name);
reqMap.put(HandlerRepository.TRIGGER_TIMESTAMP, String.valueOf(System.currentTimeMillis()));
reqMap.put(HandlerParamEnum.TIMESTAMP.name(), String.valueOf(System.currentTimeMillis()));
reqMap.put(HandlerParamEnum.ACTION.name(), ActionEnum.KILL.name());
reqMap.put(HandlerParamEnum.EXECUTOR_HANDLER.name(), log.getExecutorHandler());
reqMap.put(HandlerParamEnum.JOB_GROUP.name(), log.getJobGroup());
reqMap.put(HandlerParamEnum.JOB_NAME.name(), log.getJobName());
RemoteCallBack callBack = HttpUtil.post(handler_address, reqMap);
RemoteCallBack callBack = HttpUtil.post(HttpUtil.addressToUrl(log.getExecutorAddress()), reqMap);
if (HttpUtil.RemoteCallBack.SUCCESS.equals(callBack.getStatus())) {
log.setHandleStatus(HttpUtil.RemoteCallBack.FAIL);
log.setHandleMsg("人为操作主动终止");
......
package com.xxl.job.admin.core.jobbean;
import java.text.MessageFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
......@@ -16,10 +16,10 @@ import com.xxl.job.admin.core.model.XxlJobInfo;
import com.xxl.job.admin.core.model.XxlJobLog;
import com.xxl.job.admin.core.thread.JobMonitorHelper;
import com.xxl.job.admin.core.util.DynamicSchedulerUtil;
import com.xxl.job.core.handler.HandlerRepository;
import com.xxl.job.core.handler.HandlerRepository.ActionEnum;
import com.xxl.job.core.handler.HandlerRepository.HandlerParamEnum;
import com.xxl.job.core.util.HttpUtil;
import com.xxl.job.core.util.HttpUtil.RemoteCallBack;
import com.xxl.job.core.util.JacksonUtil;
/**
* http job bean
......@@ -30,14 +30,12 @@ import com.xxl.job.core.util.JacksonUtil;
public class RemoteHttpJobBean extends QuartzJobBean {
private static Logger logger = LoggerFactory.getLogger(RemoteHttpJobBean.class);
@SuppressWarnings("unchecked")
@Override
protected void executeInternal(JobExecutionContext context)
throws JobExecutionException {
JobKey jobKey = context.getTrigger().getJobKey();
XxlJobInfo jobInfo = DynamicSchedulerUtil.xxlJobInfoDao.load(jobKey.getGroup(), jobKey.getName());
HashMap<String, String> jobDataMap = (HashMap<String, String>) JacksonUtil.readValueRefer(jobInfo.getJobData(), Map.class);
// save log
XxlJobLog jobLog = new XxlJobLog();
jobLog.setJobGroup(jobInfo.getJobGroup());
......@@ -45,35 +43,30 @@ public class RemoteHttpJobBean extends QuartzJobBean {
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);
logger.info(">>>>>>>>>>> xxl-job trigger start, jobId:{}", jobLog.getId());
// trigger request
HashMap<String, String> params = new HashMap<String, String>();
params.put(HandlerRepository.TRIGGER_TIMESTAMP, String.valueOf(System.currentTimeMillis()));
params.put(HandlerRepository.NAMESPACE, HandlerRepository.NameSpaceEnum.RUN.name());
params.put(HandlerRepository.TRIGGER_LOG_ID, String.valueOf(jobLog.getId()));
params.put(HandlerRepository.TRIGGER_LOG_ADDRESS, XxlJobLogCallbackServer.getTrigger_log_address());
params.put(HandlerParamEnum.TIMESTAMP.name(), String.valueOf(System.currentTimeMillis()));
params.put(HandlerParamEnum.ACTION.name(), ActionEnum.RUN.name());
params.put(HandlerRepository.HANDLER_NAME, jobDataMap.get(HandlerRepository.HANDLER_NAME));
params.put(HandlerRepository.HANDLER_PARAMS, jobDataMap.get(HandlerRepository.HANDLER_PARAMS));
params.put(HandlerParamEnum.LOG_ADDRESS.name(), XxlJobLogCallbackServer.getTrigger_log_address());
params.put(HandlerParamEnum.LOG_ID.name(), String.valueOf(jobLog.getId()));
params.put(HandlerRepository.HANDLER_GLUE_SWITCH, String.valueOf(jobInfo.getGlueSwitch()));
params.put(HandlerRepository.HANDLER_JOB_GROUP, jobInfo.getJobGroup());
params.put(HandlerRepository.HANDLER_JOB_NAME, jobInfo.getJobName());
params.put(HandlerParamEnum.EXECUTOR_HANDLER.name(), jobInfo.getExecutorHandler());
params.put(HandlerParamEnum.EXECUTOR_PARAMS.name(), jobInfo.getExecutorParam());
params.put(HandlerParamEnum.GLUE_SWITCH.name(), String.valueOf(jobInfo.getGlueSwitch()));
params.put(HandlerParamEnum.JOB_GROUP.name(), jobInfo.getJobGroup());
params.put(HandlerParamEnum.JOB_NAME.name(), jobInfo.getJobName());
// handler address, jetty (servlet dead)
String handler_address = jobDataMap.get(HandlerRepository.HANDLER_ADDRESS);
RemoteCallBack callback = HttpUtil.post(HttpUtil.addressToUrl(handler_address), params);
logger.info(">>>>>>>>>>> xxl-job trigger http response, jobLog.id:{}, jobLog:{}, callback:{}", jobLog.getId(), jobLog, callback);
// failover trigger
RemoteCallBack callback = failoverTrigger(jobInfo.getExecutorAddress(), params, jobLog);
jobLog.setExecutorHandler(jobInfo.getGlueSwitch()==0?jobInfo.getExecutorHandler():"GLUE任务");
jobLog.setExecutorParam(jobInfo.getExecutorParam());
logger.info(">>>>>>>>>>> xxl-job failoverTrigger response, jobId:{}, callback:{}", jobLog.getId(), callback);
// update trigger info
jobLog.setTriggerTime(new Date());
jobLog.setTriggerStatus(callback.getStatus());
......@@ -83,7 +76,45 @@ public class RemoteHttpJobBean extends QuartzJobBean {
// monitor triger
JobMonitorHelper.monitor(jobLog.getId());
logger.info(">>>>>>>>>>> xxl-job trigger end, jobLog.id:{}, jobLog:{}", jobLog.getId(), jobLog);
logger.info(">>>>>>>>>>> xxl-job trigger end, jobId:{}", jobLog.getId());
}
/**
* failover for trigger remote address
* @param addressArr
* @return
*/
public RemoteCallBack failoverTrigger(String handler_address, HashMap<String, String> handler_params, XxlJobLog jobLog){
if (handler_address.split(",").length > 1) {
String failoverMessage = "";
for (String address : handler_address.split(",")) {
HashMap<String, String> params = new HashMap<String, String>();
params.put(HandlerParamEnum.TIMESTAMP.name(), String.valueOf(System.currentTimeMillis()));
params.put(HandlerParamEnum.ACTION.name(), ActionEnum.BEAT.name());
RemoteCallBack beatResult = HttpUtil.post(HttpUtil.addressToUrl(address), params);
failoverMessage += MessageFormat.format("BEAT running, <br>>>>[address] : {0}, <br>>>>[status] : {1}, <br>>>>[msg] : {2} <br><hr>", address, beatResult.getStatus(), beatResult.getMsg());
if (RemoteCallBack.SUCCESS.equals(beatResult.getStatus())) {
jobLog.setExecutorAddress(address);
RemoteCallBack triggerCallback = HttpUtil.post(HttpUtil.addressToUrl(address), handler_params);
triggerCallback.setStatus(RemoteCallBack.SUCCESS);
failoverMessage += MessageFormat.format("Trigger running, <br>>>>[address] : {0}, <br>>>>[status] : {1}, <br>>>>[msg] : {2} <br><hr>", address, triggerCallback.getStatus(), triggerCallback.getMsg());
triggerCallback.setMsg(failoverMessage);
return triggerCallback;
}
}
RemoteCallBack result = new RemoteCallBack();
result.setStatus(RemoteCallBack.FAIL);
result.setMsg(failoverMessage);
return result;
} else {
jobLog.setExecutorAddress(handler_address);
RemoteCallBack triggerCallback = HttpUtil.post(HttpUtil.addressToUrl(handler_address), handler_params);
String failoverMessage = MessageFormat.format("Trigger running, <br>>>>[address] : {0}, <br>>>>[status] : {1}, <br>>>>[msg] : {2} <br><hr>", handler_address, triggerCallback.getStatus(), triggerCallback.getMsg());
triggerCallback.setMsg(failoverMessage);
return triggerCallback;
}
}
}
\ No newline at end of file
......@@ -15,7 +15,7 @@ public class XxlJobInfo {
private String jobCron; // 任务执行CRON表达式 【base on quartz】
private String jobDesc;
private String jobClass; // 任务执行JobBean 【base on quartz】
private String jobData; // 任务执行数据 Map-JSON-String
//private String jobData; // 任务执行数据 Map-JSON-String
private Date addTime;
private Date updateTime;
......@@ -24,6 +24,10 @@ public class XxlJobInfo {
private String alarmEmail; // 报警邮件
private int alarmThreshold; // 报警阀值
private String executorAddress; // 执行器地址,有多个则逗号分隔
private String executorHandler; // 执行器,任务Handler名称
private String executorParam; // 执行器,任务参数
private int glueSwitch; // GLUE模式开关:0-否,1-是
private String glueSource; // GLUE源代码
private String glueRemark; // GLUE备注
......@@ -79,14 +83,6 @@ public class XxlJobInfo {
this.jobClass = jobClass;
}
public String getJobData() {
return jobData;
}
public void setJobData(String jobData) {
this.jobData = jobData;
}
public Date getAddTime() {
return addTime;
}
......@@ -127,12 +123,28 @@ public class XxlJobInfo {
this.alarmThreshold = alarmThreshold;
}
public String getJobStatus() {
return jobStatus;
public String getExecutorAddress() {
return executorAddress;
}
public void setJobStatus(String jobStatus) {
this.jobStatus = jobStatus;
public void setExecutorAddress(String executorAddress) {
this.executorAddress = executorAddress;
}
public String getExecutorHandler() {
return executorHandler;
}
public void setExecutorHandler(String executorHandler) {
this.executorHandler = executorHandler;
}
public String getExecutorParam() {
return executorParam;
}
public void setExecutorParam(String executorParam) {
this.executorParam = executorParam;
}
public int getGlueSwitch() {
......@@ -159,13 +171,12 @@ public class XxlJobInfo {
this.glueRemark = glueRemark;
}
@Override
public String toString() {
return "XxlJobInfo [id=" + id + ", jobGroup=" + jobGroup + ", jobName=" + jobName + ", jobCron=" + jobCron
+ ", jobDesc=" + jobDesc + ", jobClass=" + jobClass + ", jobData=" + jobData + ", addTime=" + addTime
+ ", updateTime=" + updateTime + ", author=" + author + ", alarmEmail=" + alarmEmail
+ ", alarmThreshold=" + alarmThreshold + ", glueSwitch=" + glueSwitch + ", glueSource=" + glueSource
+ ", glueRemark=" + glueRemark + ", jobStatus=" + jobStatus + "]";
public String getJobStatus() {
return jobStatus;
}
public void setJobStatus(String jobStatus) {
this.jobStatus = jobStatus;
}
}
......@@ -16,7 +16,10 @@ public class XxlJobLog {
private String jobCron;
private String jobDesc;
private String jobClass;
private String jobData;
private String executorAddress; // 执行器地址,有多个则逗号分隔
private String executorHandler; // 执行器,任务Handler名称
private String executorParam; // 执行器,任务参数
// trigger info
private Date triggerTime;
......@@ -27,6 +30,7 @@ public class XxlJobLog {
private Date handleTime;
private String handleStatus;
private String handleMsg;
public int getId() {
return id;
}
......@@ -63,11 +67,23 @@ public class XxlJobLog {
public void setJobClass(String jobClass) {
this.jobClass = jobClass;
}
public String getJobData() {
return jobData;
public String getExecutorAddress() {
return executorAddress;
}
public void setExecutorAddress(String executorAddress) {
this.executorAddress = executorAddress;
}
public String getExecutorHandler() {
return executorHandler;
}
public void setJobData(String jobData) {
this.jobData = jobData;
public void setExecutorHandler(String executorHandler) {
this.executorHandler = executorHandler;
}
public String getExecutorParam() {
return executorParam;
}
public void setExecutorParam(String executorParam) {
this.executorParam = executorParam;
}
public Date getTriggerTime() {
return triggerTime;
......@@ -106,12 +122,4 @@ public class XxlJobLog {
this.handleMsg = handleMsg;
}
@Override
public String toString() {
return "XxlJobLog [id=" + id + ", jobGroup=" + jobGroup + ", jobName=" + jobName + ", jobCron=" + jobCron
+ ", jobDesc=" + jobDesc + ", jobClass=" + jobClass + ", jobData=" + jobData + ", triggerTime="
+ triggerTime + ", triggerStatus=" + triggerStatus + ", triggerMsg=" + triggerMsg + ", handleTime="
+ handleTime + ", handleStatus=" + handleStatus + ", handleMsg=" + handleMsg + "]";
}
}
......@@ -12,7 +12,6 @@ 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;
......@@ -35,7 +34,6 @@ import com.xxl.job.admin.core.callback.XxlJobLogCallbackServer;
import com.xxl.job.admin.core.model.XxlJobInfo;
import com.xxl.job.admin.dao.IXxlJobInfoDao;
import com.xxl.job.admin.dao.IXxlJobLogDao;
import com.xxl.job.core.util.JacksonUtil;
/**
* base quartz scheduler util
......@@ -151,7 +149,7 @@ public final class DynamicSchedulerUtil implements ApplicationContextAware, Init
}
// addJob 新增
@SuppressWarnings("unchecked")
@SuppressWarnings("unchecked")
public static boolean addJob(XxlJobInfo jobInfo) throws SchedulerException {
// TriggerKey : name + group
TriggerKey triggerKey = TriggerKey.triggerKey(jobInfo.getJobName(), jobInfo.getJobGroup());
......@@ -176,10 +174,11 @@ public final class DynamicSchedulerUtil implements ApplicationContextAware, Init
}
JobDetail jobDetail = JobBuilder.newJob(jobClass_).withIdentity(jobKey).build();
if (jobInfo.getJobData()!=null) {
/*if (jobInfo.getJobData()!=null) {
JobDataMap jobDataMap = jobDetail.getJobDataMap();
jobDataMap.putAll(JacksonUtil.readValue(jobInfo.getJobData(), Map.class)); // JobExecutionContext context.getMergedJobDataMap().get("mailGuid");
}
jobDataMap.putAll(JacksonUtil.readValue(jobInfo.getJobData(), Map.class));
// JobExecutionContext context.getMergedJobDataMap().get("mailGuid");
}*/
// schedule : jobDetail + cronTrigger
Date date = scheduler.scheduleJob(jobDetail, cronTrigger);
......@@ -189,7 +188,6 @@ public final class DynamicSchedulerUtil implements ApplicationContextAware, Init
}
// reschedule
@SuppressWarnings("unchecked")
public static boolean rescheduleJob(XxlJobInfo jobInfo) throws SchedulerException {
// TriggerKey valid if_exists
......@@ -210,9 +208,9 @@ public final class DynamicSchedulerUtil implements ApplicationContextAware, Init
// JobDetail-JobDataMap fresh
JobDetail jobDetail = scheduler.getJobDetail(jobKey);
JobDataMap jobDataMap = jobDetail.getJobDataMap();
/*JobDataMap jobDataMap = jobDetail.getJobDataMap();
jobDataMap.clear();
jobDataMap.putAll(JacksonUtil.readValue(jobInfo.getJobData(), Map.class));
jobDataMap.putAll(JacksonUtil.readValue(jobInfo.getJobData(), Map.class));*/
// Trigger fresh
HashSet<Trigger> triggerSet = new HashSet<Trigger>();
......
......@@ -63,15 +63,12 @@ public class XxlJobLogDaoImpl implements IXxlJobLogDao {
@Override
public int save(XxlJobLog xxlJobLog) {
if (xxlJobLog!=null && xxlJobLog.getJobData().length()>2000) {
xxlJobLog.setJobData(xxlJobLog.getJobData().substring(0, 2000));
}
return sqlSessionTemplate.insert("XxlJobLogMapper.save", xxlJobLog);
}
@Override
public int updateTriggerInfo(XxlJobLog xxlJobLog) {
if (xxlJobLog!=null && xxlJobLog.getTriggerMsg()!=null && xxlJobLog.getTriggerMsg().length()>2000) {
if (xxlJobLog.getTriggerMsg()!=null && xxlJobLog.getTriggerMsg().length()>2000) {
xxlJobLog.setTriggerMsg(xxlJobLog.getTriggerMsg().substring(0, 2000));
}
return sqlSessionTemplate.update("XxlJobLogMapper.updateTriggerInfo", xxlJobLog);
......@@ -79,7 +76,7 @@ public class XxlJobLogDaoImpl implements IXxlJobLogDao {
@Override
public int updateHandleInfo(XxlJobLog xxlJobLog) {
if (xxlJobLog!=null && xxlJobLog.getHandleMsg()!=null && xxlJobLog.getHandleMsg().length()>2000) {
if (xxlJobLog.getHandleMsg()!=null && xxlJobLog.getHandleMsg().length()>2000) {
xxlJobLog.setHandleMsg(xxlJobLog.getHandleMsg().substring(0, 2000));
}
return sqlSessionTemplate.update("XxlJobLogMapper.updateHandleInfo", xxlJobLog);
......
package com.xxl.job.admin.service;
import java.util.Map;
import com.xxl.job.admin.core.model.ReturnT;
/**
* core job service for xxl-job
*
* @author xuxueli 2016-5-28 15:30:33
*/
public interface IXxlJobService {
public Map<String, Object> pageList(int start, int length, String jobGroup, String jobName, String filterTime);
public ReturnT<String> add(String jobGroup, String jobName, String jobCron, String jobDesc,
String executorAddress, String executorHandler, String executorParam,
String author, String alarmEmail, int alarmThreshold,
int glueSwitch, String glueSource, String glueRemark);
public ReturnT<String> reschedule(String jobGroup, String jobName, String jobCron, String jobDesc,
String handler_address, String handler_name, String handler_params,
String author, String alarmEmail, int alarmThreshold, int glueSwitch);
public ReturnT<String> remove(String jobGroup, String jobName);
public ReturnT<String> pause(String jobGroup, String jobName);
public ReturnT<String> resume(String jobGroup, String jobName);
public ReturnT<String> triggerJob(String jobGroup, String jobName);
}
package com.xxl.job.admin.service.impl;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.annotation.Resource;
import org.apache.commons.lang.StringUtils;
import org.quartz.CronExpression;
import org.quartz.SchedulerException;
import org.springframework.stereotype.Service;
import com.xxl.job.admin.core.constant.Constants.JobGroupEnum;
import com.xxl.job.admin.core.jobbean.RemoteHttpJobBean;
import com.xxl.job.admin.core.model.ReturnT;
import com.xxl.job.admin.core.model.XxlJobInfo;
import com.xxl.job.admin.core.util.DynamicSchedulerUtil;
import com.xxl.job.admin.dao.IXxlJobInfoDao;
import com.xxl.job.admin.dao.IXxlJobLogDao;
import com.xxl.job.admin.dao.IXxlJobLogGlueDao;
import com.xxl.job.admin.service.IXxlJobService;
/**
* core job service for xxl-job
* @author xuxueli 2016-5-28 15:30:33
*/
@Service
public class XxlJobServiceImpl implements IXxlJobService {
@Resource
private IXxlJobInfoDao xxlJobInfoDao;
@Resource
public IXxlJobLogDao xxlJobLogDao;
@Resource
private IXxlJobLogGlueDao xxlJobLogGlueDao;
@Override
public Map<String, Object> pageList(int start, int length, String jobGroup, String jobName, String filterTime) {
// page list
List<XxlJobInfo> list = xxlJobInfoDao.pageList(start, length, jobGroup, jobName);
int list_count = xxlJobInfoDao.pageListCount(start, length, jobGroup, jobName);
// fill job info
if (list!=null && list.size()>0) {
for (XxlJobInfo jobInfo : list) {
DynamicSchedulerUtil.fillJobInfo(jobInfo);
}
}
// package result
Map<String, Object> maps = new HashMap<String, Object>();
maps.put("recordsTotal", list_count); // 总记录数
maps.put("recordsFiltered", list_count); // 过滤后的总记录数
maps.put("data", list); // 分页列表
return maps;
}
@Override
public ReturnT<String> add(String jobGroup, String jobName, String jobCron, String jobDesc,
String executorAddress, String executorHandler, String executorParam,
String author, String alarmEmail, int alarmThreshold,
int glueSwitch, String glueSource, String glueRemark) {
// valid
if (JobGroupEnum.match(jobGroup) == null) {
return new ReturnT<String>(500, "请选择“任务组”");
}
if (StringUtils.isBlank(jobName)) {
return new ReturnT<String>(500, "请输入“任务名”");
}
if (!CronExpression.isValidExpression(jobCron)) {
return new ReturnT<String>(500, "请输入格式正确的“corn”");
}
if (StringUtils.isBlank(jobDesc)) {
return new ReturnT<String>(500, "请输入“任务描述”");
}
if (StringUtils.isBlank(executorAddress)) {
return new ReturnT<String>(500, "请输入“执行器地址”");
}
if (glueSwitch==0 && StringUtils.isBlank(executorHandler)) {
return new ReturnT<String>(500, "请输入“jobHandler”");
}
if (StringUtils.isBlank(author)) {
return new ReturnT<String>(500, "请输入“负责人”");
}
if (StringUtils.isBlank(alarmEmail)) {
return new ReturnT<String>(500, "请输入“报警邮件”");
}
if (alarmThreshold < 0) {
alarmThreshold = 0;
}
try {
if (DynamicSchedulerUtil.checkExists(jobName, jobGroup)) {
return new ReturnT<String>(500, "此任务已存在,请更换任务组或任务名");
}
} catch (SchedulerException e1) {
e1.printStackTrace();
return new ReturnT<String>(500, "此任务已存在,请更换任务组或任务名");
}
// Backup to the database
XxlJobInfo jobInfo = new XxlJobInfo();
jobInfo.setJobGroup(jobGroup);
jobInfo.setJobName(jobName);
jobInfo.setJobCron(jobCron);
jobInfo.setJobDesc(jobDesc);
jobInfo.setJobClass(RemoteHttpJobBean.class.getName());
jobInfo.setAuthor(author);
jobInfo.setAlarmEmail(alarmEmail);
jobInfo.setAlarmThreshold(alarmThreshold);
jobInfo.setGlueSwitch(glueSwitch);
jobInfo.setGlueSource(glueSource);
jobInfo.setGlueRemark(glueRemark);
jobInfo.setExecutorAddress(executorAddress);
jobInfo.setExecutorHandler(executorHandler);
jobInfo.setExecutorParam(executorParam);
xxlJobInfoDao.save(jobInfo);
try {
// add job 2 quartz
boolean result = DynamicSchedulerUtil.addJob(jobInfo);
if (result) {
return ReturnT.SUCCESS;
} else {
xxlJobInfoDao.delete(jobGroup, jobName);
return new ReturnT<String>(500, "新增任务失败");
}
} catch (SchedulerException e) {
e.printStackTrace();
}
return ReturnT.FAIL;
}
@Override
public ReturnT<String> reschedule(String jobGroup, String jobName, String jobCron, String jobDesc,
String executorAddress, String executorHandler, String executorParam,
String author, String alarmEmail, int alarmThreshold, int glueSwitch) {
// valid
if (JobGroupEnum.match(jobGroup) == null) {
return new ReturnT<String>(500, "请选择“任务组”");
}
if (StringUtils.isBlank(jobName)) {
return new ReturnT<String>(500, "请输入“任务名”");
}
if (!CronExpression.isValidExpression(jobCron)) {
return new ReturnT<String>(500, "请输入格式正确的“corn”");
}
if (StringUtils.isBlank(jobDesc)) {
return new ReturnT<String>(500, "请输入“任务描述”");
}
if (StringUtils.isBlank(executorAddress)) {
return new ReturnT<String>(500, "请输入“执行器地址”");
}
if (glueSwitch==0 && StringUtils.isBlank(executorHandler)) {
return new ReturnT<String>(500, "请输入“jobHandler”");
}
if (StringUtils.isBlank(author)) {
return new ReturnT<String>(500, "请输入“负责人”");
}
if (StringUtils.isBlank(alarmEmail)) {
return new ReturnT<String>(500, "请输入“报警邮件”");
}
if (alarmThreshold < 0) {
alarmThreshold = 0;
}
XxlJobInfo jobInfo = xxlJobInfoDao.load(jobGroup, jobName);
jobInfo.setJobCron(jobCron);
jobInfo.setJobDesc(jobDesc);
jobInfo.setAuthor(author);
jobInfo.setAlarmEmail(alarmEmail);
jobInfo.setAlarmThreshold(alarmThreshold);
jobInfo.setGlueSwitch(glueSwitch);
jobInfo.setExecutorAddress(executorAddress);
jobInfo.setExecutorHandler(executorHandler);
jobInfo.setExecutorParam(executorParam);
try {
// fresh quartz
DynamicSchedulerUtil.rescheduleJob(jobInfo);
// fresh db
xxlJobInfoDao.update(jobInfo);
return ReturnT.SUCCESS;
} catch (SchedulerException e) {
e.printStackTrace();
}
return ReturnT.FAIL;
}
@Override
public ReturnT<String> remove(String jobGroup, String jobName) {
try {
DynamicSchedulerUtil.removeJob(jobName, jobGroup);
xxlJobInfoDao.delete(jobGroup, jobName);
xxlJobLogDao.delete(jobGroup, jobName);
xxlJobLogGlueDao.delete(jobGroup, jobName);
return ReturnT.SUCCESS;
} catch (SchedulerException e) {
e.printStackTrace();
}
return ReturnT.FAIL;
}
@Override
public ReturnT<String> pause(String jobGroup, String jobName) {
try {
DynamicSchedulerUtil.pauseJob(jobName, jobGroup); // jobStatus do not store
return ReturnT.SUCCESS;
} catch (SchedulerException e) {
e.printStackTrace();
return ReturnT.FAIL;
}
}
@Override
public ReturnT<String> resume(String jobGroup, String jobName) {
try {
DynamicSchedulerUtil.resumeJob(jobName, jobGroup);
return ReturnT.SUCCESS;
} catch (SchedulerException e) {
e.printStackTrace();
return ReturnT.FAIL;
}
}
@Override
public ReturnT<String> triggerJob(String jobGroup, String jobName) {
try {
DynamicSchedulerUtil.triggerJob(jobName, jobGroup);
return ReturnT.SUCCESS;
} catch (SchedulerException e) {
e.printStackTrace();
return ReturnT.FAIL;
}
}
}
......@@ -9,7 +9,7 @@
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util.xsd">
<context:component-scan base-package="com.xxl.job.admin.dao" />
<context:component-scan base-package="com.xxl.job.admin.service, com.xxl.job.admin.dao" />
<bean id="freemarkerConfig" class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
<property name="templateLoaderPath" value="/WEB-INF/template/" />
......
......@@ -48,7 +48,6 @@
</bean>
<!-- part 2 :for tx -->
<!--
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
......@@ -68,9 +67,8 @@
</tx:advice>
<aop:config>
<aop:pointcut id="txoperation" expression="execution(* com.xxl.job.admin.service.imp.*.*(..))" />
<aop:pointcut id="txoperation" expression="execution(* com.xxl.job.admin.service.impl.*.*(..))" />
<aop:advisor pointcut-ref="txoperation" advice-ref="txAdvice" />
</aop:config>
-->
</beans>
\ No newline at end of file
......@@ -11,7 +11,6 @@
<result column="job_cron" property="jobCron" />
<result column="job_desc" property="jobDesc" />
<result column="job_class" property="jobClass" />
<result column="job_data" property="jobData" />
<result column="add_time" property="addTime" />
<result column="update_time" property="updateTime" />
......@@ -20,6 +19,10 @@
<result column="alarm_email" property="alarmEmail" />
<result column="alarm_threshold" property="alarmThreshold" />
<result column="executor_address" property="executorAddress" />
<result column="executor_handler" property="executorHandler" />
<result column="executor_param" property="executorParam" />
<result column="glue_switch" property="glueSwitch" />
<result column="glue_source" property="glueSource" />
<result column="glue_remark" property="glueRemark" />
......@@ -32,12 +35,14 @@
t.job_cron,
t.job_desc,
t.job_class,
t.job_data,
t.add_time,
t.update_time,
t.author,
t.alarm_email,
t.alarm_threshold,
t.executor_address,
t.executor_handler,
t.executor_param,
t.glue_switch,
t.glue_source,
t.glue_remark
......@@ -78,12 +83,14 @@
job_cron,
job_desc,
job_class,
job_data,
add_time,
update_time,
author,
alarm_email,
alarm_threshold,
executor_address,
executor_handler,
executor_param,
glue_switch,
glue_source,
glue_remark
......@@ -93,12 +100,14 @@
#{jobCron},
#{jobDesc},
#{jobClass},
#{jobData},
NOW(),
NOW(),
#{author},
#{alarmEmail},
#{alarmThreshold},
#{executorAddress},
#{executorHandler},
#{executorParam},
#{glueSwitch},
#{glueSource},
#{glueRemark}
......@@ -120,11 +129,13 @@
SET
job_cron = #{jobCron},
job_desc = #{jobDesc},
job_data = #{jobData},
update_time = NOW(),
author = #{author},
alarm_email = #{alarmEmail},
alarm_threshold = #{alarmThreshold},
executor_address = #{executorAddress},
executor_handler = #{executorHandler},
executor_param = #{executorParam},
glue_switch = #{glueSwitch},
glue_source = #{glueSource},
glue_remark = #{glueRemark}
......
......@@ -11,7 +11,10 @@
<result column="job_cron" property="jobCron" />
<result column="job_desc" property="jobDesc" />
<result column="job_class" property="jobClass" />
<result column="job_data" property="jobData" />
<result column="executor_address" property="executorAddress" />
<result column="executor_handler" property="executorHandler" />
<result column="executor_param" property="executorParam" />
<result column="trigger_time" property="triggerTime" />
<result column="trigger_status" property="triggerStatus" />
......@@ -20,6 +23,7 @@
<result column="handle_time" property="handleTime" />
<result column="handle_status" property="handleStatus" />
<result column="handle_msg" property="handleMsg" />
</resultMap>
<sql id="Base_Column_List">
......@@ -29,7 +33,9 @@
t.job_cron,
t.job_desc,
t.job_class,
t.job_data,
t.executor_address,
t.executor_handler,
t.executor_param,
t.trigger_time,
t.trigger_status,
t.trigger_msg,
......@@ -98,14 +104,18 @@
`job_cron`,
`job_desc`,
`job_class`,
`job_data`
`executor_address`,
`executor_handler`,
`executor_param`
) VALUES (
#{jobGroup},
#{jobName},
#{jobCron},
#{jobDesc},
#{jobClass},
#{jobData}
#{executorAddress},
#{executorHandler},
#{executorParam}
);
<selectKey resultType="java.lang.Integer" order="AFTER" keyProperty="id">
SELECT LAST_INSERT_ID()
......@@ -117,7 +127,10 @@
SET
`trigger_time`= #{triggerTime},
`trigger_status`= #{triggerStatus},
`trigger_msg`= #{triggerMsg}
`trigger_msg`= #{triggerMsg},
`executor_address`= #{executorAddress},
`executor_handler`= #{executorHandler},
`executor_param`= #{executorParam}
WHERE `id`= #{id}
</update>
......
......@@ -72,7 +72,9 @@
<th name="jobDesc" >描述</th>
<th name="jobCron" >Cron</th>
<th name="jobClass" >JobBean</th>
<th name="jobData" >任务数据</th>
<th name="executorAddress" >执行器地址</th>
<th name="executorHandler" >JobHandler</th>
<th name="executorParam" >任务参数</th>
<th name="addTime" >新增时间</th>
<th name="updateTime" >更新时间</th>
<th name="author" >负责人</th>
......@@ -126,13 +128,13 @@
</div>
<div class="form-group">
<label for="lastname" class="col-sm-2 control-label">执行器地址<font color="red">*</font></label>
<div class="col-sm-4"><input type="text" class="form-control" name="handler_address" placeholder="请输入“执行器地址”" maxlength="200" ></div>
<div class="col-sm-4"><input type="text" class="form-control" name="executorAddress" placeholder="请输入“执行器地址”,多个地址逗号分隔" maxlength="200" ></div>
<label for="lastname" class="col-sm-2 control-label">jobHandler<font color="red">*</font></label>
<div class="col-sm-4"><input type="text" class="form-control" name="handler_name" placeholder="请输入“jobHandler”" maxlength="200" ></div>
<div class="col-sm-4"><input type="text" class="form-control" name="executorHandler" placeholder="请输入“jobHandler”" maxlength="200" ></div>
</div>
<div class="form-group">
<label for="firstname" class="col-sm-2 control-label">执行参数<font color="black">*</font></label>
<div class="col-sm-4"><input type="text" class="form-control" name="handler_params" placeholder="请输入“执行参数”" maxlength="100" ></div>
<div class="col-sm-4"><input type="text" class="form-control" name="executorParam" placeholder="请输入“执行参数”" maxlength="100" ></div>
<label for="lastname" class="col-sm-2 control-label">负责人<font color="red">*</font></label>
<div class="col-sm-4"><input type="text" class="form-control" name="author" placeholder="请输入“负责人”" maxlength="200" ></div>
</div>
......@@ -207,13 +209,13 @@ public class DemoJobHandler extends IJobHandler {
<div class="form-group">
<label for="lastname" class="col-sm-2 control-label">执行器地址<font color="red">*</font></label>
<div class="col-sm-4"><input type="text" class="form-control" name="handler_address" placeholder="请输入“执行器地址”" maxlength="200" ></div>
<div class="col-sm-4"><input type="text" class="form-control" name="executorAddress" placeholder="请输入“执行器地址”,多个地址逗号分隔" maxlength="200" ></div>
<label for="lastname" class="col-sm-2 control-label">jobHandler<font color="red">*</font></label>
<div class="col-sm-4"><input type="text" class="form-control" name="handler_name" placeholder="请输入“jobHandler”" maxlength="200" ></div>
<div class="col-sm-4"><input type="text" class="form-control" name="executorHandler" placeholder="请输入“jobHandler”" maxlength="200" ></div>
</div>
<div class="form-group">
<label for="firstname" class="col-sm-2 control-label">执行参数<font color="black">*</font></label>
<div class="col-sm-4"><input type="text" class="form-control" name="handler_params" placeholder="请输入“执行参数”" maxlength="100" ></div>
<div class="col-sm-4"><input type="text" class="form-control" name="executorParam" placeholder="请输入“执行参数”" maxlength="100" ></div>
<label for="lastname" class="col-sm-2 control-label">负责人<font color="red">*</font></label>
<div class="col-sm-4"><input type="text" class="form-control" name="author" placeholder="请输入“负责人”" maxlength="200" ></div>
</div>
......
......@@ -78,7 +78,9 @@
<th name="jobCron" >Cron</th>
<th name="jobDesc" >描述</th>
<th name="jobClass" >JobBean</th>
<th name="jobData" >任务数据</th>
<th name="executorAddress" >执行器地址</th>
<th name="executorHandler" >JobHandler</th>
<th name="executorParam" >任务参数</th>
<th name="triggerTime" >调度时间</th>
<th name="triggerStatus" >调度结果</th>
<th name="triggerMsg" >调度日志</th>
......
......@@ -33,17 +33,9 @@ $(function() {
{ "data": 'jobDesc', "visible" : true},
{ "data": 'jobCron', "visible" : true},
{ "data": 'jobClass', "visible" : false},
{
"data": 'jobData',
"visible" : true,
"render": function ( data, type, row ) {
var _jobData = eval('(' + data + ')'); // row.jobData
var html = "<p title='" + data + "'>执行器:" + _jobData.handler_name +
"<br>执行参数:" + _jobData.handler_params +
"<br>执行器地址:" + _jobData.handler_address + "</p>";
return html;
}
},
{ "data": 'executorAddress', "visible" : false},
{ "data": 'executorHandler', "visible" : false},
{ "data": 'executorParam', "visible" : false},
{
"data": 'addTime',
"visible" : false,
......@@ -90,15 +82,12 @@ $(function() {
var logUrl = base_url +'/joblog?jobGroup='+ row.jobGroup +'&jobName='+ row.jobName;
// log url
var codeHtml = "";
if(row.glueSwitch != 0){
var codeBtn = "";
if(row.glueSwitch > 0){
var codeUrl = base_url +'/jobcode?jobGroup='+ row.jobGroup +'&jobName='+ row.jobName;
codeHtml = '<button class="btn btn-warning btn-xs" type="button" onclick="javascript:window.open(\'' + codeUrl + '\')" >GLUE</button> '
codeBtn = '<button class="btn btn-warning btn-xs" type="button" onclick="javascript:window.open(\'' + codeUrl + '\')" >GLUE</button> '
}
// job data
var jobDataMap = eval('(' + row.jobData + ')');
var html = '<p id="'+ row.id +'" '+
' jobGroup="'+ row.jobGroup +'" '+
' jobName="'+ row.jobName +'" '+
......@@ -106,9 +95,9 @@ $(function() {
' jobDesc="'+ row.jobDesc +'" '+
' jobClass="'+ row.jobClass +'" '+
' jobData="'+ row.jobData +'" '+
' handler_params="'+jobDataMap.handler_params +'" '+
' handler_address="'+ jobDataMap.handler_address +'" '+
' handler_name="'+ jobDataMap.handler_name +'" '+
' executorAddress="'+row.executorAddress +'" '+
' executorHandler="'+ row.executorHandler +'" '+
' executorParam="'+ row.executorParam +'" '+
' author="'+ row.author +'" '+
' alarmEmail="'+ row.alarmEmail +'" '+
' alarmThreshold="'+ row.alarmThreshold +'" '+
......@@ -118,7 +107,7 @@ $(function() {
pause_resume +
'<button class="btn btn-primary btn-xs" type="job_del" type="button" onclick="javascript:window.open(\'' + logUrl + '\')" >日志</button><br> '+
'<button class="btn btn-warning btn-xs update" type="button">编辑</button> '+
codeHtml +
codeBtn +
'<button class="btn btn-danger btn-xs job_operate" type="job_del" type="button">删除</button> '+
'</p>';
......@@ -236,11 +225,11 @@ $(function() {
required : true ,
maxlength: 200
},
handler_address : {
executorAddress : {
required : true ,
maxlength: 200
},
handler_name : {
executorHandler : {
required : true ,
maxlength: 200
},
......@@ -271,11 +260,11 @@ $(function() {
required :"请输入“任务描述”." ,
maxlength:"“任务描述”长度不应超过200位"
},
handler_address : {
executorAddress : {
required :"请输入“执行器地址”." ,
maxlength:"“执行器地址”长度不应超过200位"
},
handler_name : {
executorHandler : {
required : "请输入“jobHandler”." ,
maxlength: "“jobHandler”长度不应超过200位"
},
......@@ -359,9 +348,9 @@ $(function() {
$("#updateModal .form input[name='jobName']").val($(this).parent('p').attr("jobName"));
$("#updateModal .form input[name='jobCron']").val($(this).parent('p').attr("jobCron"));
$("#updateModal .form input[name='jobDesc']").val($(this).parent('p').attr("jobDesc"));
$("#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='handler_params']").val($(this).parent('p').attr("handler_params"));
$("#updateModal .form input[name='executorAddress']").val($(this).parent('p').attr("executorAddress"));
$("#updateModal .form input[name='executorHandler']").val($(this).parent('p').attr("executorHandler"));
$("#updateModal .form input[name='executorParam']").val($(this).parent('p').attr("executorParam"));
$("#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"));
......@@ -393,11 +382,11 @@ $(function() {
required : true ,
maxlength: 200
},
handler_address : {
executorAddress : {
required : true ,
maxlength: 200
},
handler_name : {
executorHandler : {
required : true ,
maxlength: 200
},
......@@ -423,11 +412,11 @@ $(function() {
required :"请输入“任务描述”." ,
maxlength:"“任务描述”长度不应超过200位"
},
handler_address : {
executorAddress : {
required :"请输入“执行器地址”." ,
maxlength:"“执行器地址”长度不应超过200位"
},
handler_name : {
executorHandler : {
required : "请输入“jobHandler”." ,
maxlength: "“jobHandler”长度不应超过200位"
},
......
......@@ -60,22 +60,14 @@ $(function() {
return data;
}
},
{ "data": 'jobName'},
{ "data": 'jobName', "visible" : false},
{ "data": 'jobCron', "visible" : false},
{ "data": 'jobDesc', "visible" : false},
{ "data": 'jobClass', "visible" : false},
{
"data": 'jobData',
"visible" : true,
"render": function ( data, type, row ) {
var _jobData = eval('(' + data + ')'); // row.jobData
var html = "<p title='" + data + "'>JobHandler:" + _jobData.handler_name +
"<br>执行参数:" + _jobData.handler_params +
"<br>执行器地址:" + _jobData.handler_address + "</p>";
return data?'<a class="logMsg" href="javascript:;" >查看<span style="display:none;">'+ html +'</span></a>':"";
}
},
{ "data": 'executorAddress', "visible" : true},
{ "data": 'executorHandler', "visible" : true},
{ "data": 'executorParam', "visible" : true},
{
"data": 'triggerTime',
"render": function ( data, type, row ) {
......@@ -106,12 +98,6 @@ $(function() {
"render": function ( data, type, row ) {
// better support expression or string, not function
return function () {
// local job do not support trigger detail log, now
var _jobData = eval('(' + row.jobData + ')');
if (!_jobData.handler_address) {
return;
}
if (row.triggerStatus == 'SUCCESS'){
var temp = '<a href="javascript:;" class="logDetail" _id="'+ row.id +'">执行日志</a>';
if(!row.handleStatus){
......
......@@ -34,7 +34,6 @@ public class XxlJobInfoTest {
info.setJobName("job_name");
info.setJobCron("jobCron");
info.setJobClass("jobClass");
info.setJobData("jobData");
int count = xxlJobInfoDao.save(info);
System.out.println(count);
System.out.println(info.getId());
......@@ -48,7 +47,6 @@ public class XxlJobInfoTest {
XxlJobInfo item = xxlJobInfoDao.load(null ,"job_name");
item.setJobCron("jobCron2");
item.setJobData("jobData2");
xxlJobInfoDao.update(item);
}
......
......@@ -28,7 +28,6 @@ public class XxlJobLogTest {
xxlJobLog.setJobName("job_name");
xxlJobLog.setJobCron("jobCron");
xxlJobLog.setJobClass("jobClass");
xxlJobLog.setJobData("jobData");
int count = xxlJobLogDao.save(xxlJobLog);
System.out.println(count);
System.out.println(xxlJobLog.getId());
......
......@@ -19,20 +19,49 @@ import com.xxl.job.core.util.JacksonUtil;
public class HandlerRepository {
private static Logger logger = LoggerFactory.getLogger(HandlerRepository.class);
public static final String NAMESPACE = "namespace";
public enum NameSpaceEnum{RUN, KILL, LOG}
public static final String HANDLER_ADDRESS = "handler_address";
public static final String HANDLER_PARAMS = "handler_params";
public static final String HANDLER_GLUE_SWITCH = "handler_glue_switch";
public static final String HANDLER_NAME = "handler_name";
public static final String HANDLER_JOB_GROUP = "handler_job_group";
public static final String HANDLER_JOB_NAME = "handler_job_name";
public static final String TRIGGER_LOG_ID = "trigger_log_id";
public static final String TRIGGER_LOG_ADDRESS = "trigger_log_address";
public static final String TRIGGER_TIMESTAMP = "trigger_timestamp";
public enum HandlerParamEnum{
/**
* trigger timestamp
*/
TIMESTAMP,
/**
* trigger action
*/
ACTION,
/**
* remote executor jobhandler
*/
EXECUTOR_HANDLER,
/**
* params of jobhandler
*/
EXECUTOR_PARAMS,
/**
* switch of job, if open glue model
*/
GLUE_SWITCH,
/**
* job group
*/
JOB_GROUP,
/**
* job name
*/
JOB_NAME,
/**
* address for callback log
*/
LOG_ADDRESS,
/**
* log id
*/
LOG_ID,
/**
* log date
*/
LOG_DATE
}
public enum ActionEnum{RUN, KILL, LOG, BEAT}
public static ConcurrentHashMap<String, HandlerThread> handlerTreadMap = new ConcurrentHashMap<String, HandlerThread>();
......@@ -53,27 +82,26 @@ public class HandlerRepository {
callback.setStatus(RemoteCallBack.FAIL);
// check namespace
String namespace = _param.get(HandlerRepository.NAMESPACE);
String namespace = _param.get(HandlerParamEnum.ACTION.name());
if (namespace==null || namespace.trim().length()==0) {
callback.setMsg("param[NAMESPACE] can not be null.");
return JacksonUtil.writeValueAsString(callback);
}
// parse namespace
if (namespace.equals(HandlerRepository.NameSpaceEnum.RUN.name())) {
// encryption check
long timestamp = _param.get(HandlerRepository.TRIGGER_TIMESTAMP)!=null?Long.valueOf(_param.get(HandlerRepository.TRIGGER_TIMESTAMP)):-1;
if (System.currentTimeMillis() - timestamp > 60000) {
callback.setMsg("Timestamp check failed.");
return JacksonUtil.writeValueAsString(callback);
}
// encryption check
long timestamp = _param.get(HandlerParamEnum.TIMESTAMP.name())!=null?Long.valueOf(_param.get(HandlerParamEnum.TIMESTAMP.name())):-1;
if (System.currentTimeMillis() - timestamp > 60000) {
callback.setMsg("Timestamp check failed.");
return JacksonUtil.writeValueAsString(callback);
}
// parse namespace
if (namespace.equals(ActionEnum.RUN.name())) {
// push data to queue
String handler_glue_switch = _param.get(HandlerRepository.HANDLER_GLUE_SWITCH);
String handler_glue_switch = _param.get(HandlerParamEnum.GLUE_SWITCH.name());
HandlerThread handlerThread = null;
if ("0".equals(handler_glue_switch)) {
// bean model
String handler_name = _param.get(HandlerRepository.HANDLER_NAME);
String handler_name = _param.get(HandlerParamEnum.EXECUTOR_HANDLER.name());
if (handler_name == null || handler_name.trim().length()==0) {
callback.setMsg("bean model handler[HANDLER_NAME] not found.");
return JacksonUtil.writeValueAsString(callback);
......@@ -85,70 +113,81 @@ public class HandlerRepository {
}
} else {
// glue
String handler_job_group = _param.get(HandlerRepository.HANDLER_JOB_GROUP);
String handler_job_name = _param.get(HandlerRepository.HANDLER_JOB_NAME);
if (handler_job_group == null || handler_job_group.trim().length()==0 || handler_job_name == null || handler_job_name.trim().length()==0) {
String job_group = _param.get(HandlerParamEnum.JOB_GROUP.name());
String job_name = _param.get(HandlerParamEnum.JOB_NAME.name());
if (job_group == null || job_group.trim().length()==0 || job_name == null || job_name.trim().length()==0) {
callback.setMsg("glue model handler[job group or name] is null.");
return JacksonUtil.writeValueAsString(callback);
}
String glueHandleName = "glue_".concat(handler_job_group).concat("_").concat(handler_job_name);
String glueHandleName = "glue_".concat(job_group).concat("_").concat(job_name);
handlerThread = handlerTreadMap.get(glueHandleName);
if (handlerThread==null) {
HandlerRepository.regist(glueHandleName, new GlueJobHandler(handler_job_group, handler_job_name));
HandlerRepository.regist(glueHandleName, new GlueJobHandler(job_group, job_name));
}
handlerThread = handlerTreadMap.get(glueHandleName);
}
handlerThread.pushData(_param);
callback.setStatus(RemoteCallBack.SUCCESS);
} else if (namespace.equals(HandlerRepository.NameSpaceEnum.LOG.name())) {
String trigger_log_id = _param.get(HandlerRepository.TRIGGER_LOG_ID);
String trigger_timestamp = _param.get(HandlerRepository.TRIGGER_TIMESTAMP);
if (trigger_log_id==null || trigger_timestamp==null) {
callback.setMsg("trigger_log_id | trigger_timestamp can not be null.");
} else if (namespace.equals(ActionEnum.LOG.name())) {
String log_id = _param.get(HandlerParamEnum.LOG_ID.name());
String log_date = _param.get(HandlerParamEnum.LOG_DATE.name());
if (log_id==null || log_date==null) {
callback.setMsg("LOG_ID | LOG_DATE can not be null.");
return JacksonUtil.writeValueAsString(callback);
}
int logId = -1;
Date triggerDate = null;
try {
logId = Integer.valueOf(trigger_log_id);
triggerDate = new Date(Long.valueOf(trigger_timestamp));
logId = Integer.valueOf(log_id);
triggerDate = new Date(Long.valueOf(log_date));
} catch (Exception e) {
}
if (logId<=0 || triggerDate==null) {
callback.setMsg("trigger_log_id | trigger_timestamp is not parsed valid.");
callback.setMsg("LOG_ID | LOG_DATE parse error.");
return JacksonUtil.writeValueAsString(callback);
}
String logConteng = XxlJobFileAppender.readLog(triggerDate, trigger_log_id);
String logConteng = XxlJobFileAppender.readLog(triggerDate, log_id);
callback.setStatus(RemoteCallBack.SUCCESS);
callback.setMsg(logConteng);
} else if (namespace.equals(HandlerRepository.NameSpaceEnum.KILL.name())) {
// encryption check
long timestamp = _param.get(HandlerRepository.TRIGGER_TIMESTAMP)!=null?Long.valueOf(_param.get(HandlerRepository.TRIGGER_TIMESTAMP)):-1;
if (System.currentTimeMillis() - timestamp > 60000) {
callback.setMsg("Timestamp check failed.");
return JacksonUtil.writeValueAsString(callback);
}
} else if (namespace.equals(ActionEnum.KILL.name())) {
// kill handlerThread, and create new one
String handler_name = _param.get(HandlerRepository.HANDLER_NAME);
if (handler_name!=null && handler_name.trim().length()>0) {
HandlerThread handlerThread = handlerTreadMap.get(handler_name);
if (handlerThread != null) {
IJobHandler handler = handlerThread.getHandler();
handlerThread.toStop();
handlerThread.interrupt();
regist(handler_name, handler);
callback.setStatus(RemoteCallBack.SUCCESS);
} else {
callback.setMsg("handler[" + handler_name + "] not found.");
String handler_glue_switch = _param.get(HandlerParamEnum.GLUE_SWITCH.name());
String handlerName = null;
if ("0".equals(handler_glue_switch)) {
String executor_handler = _param.get(HandlerParamEnum.EXECUTOR_HANDLER.name());
if (executor_handler==null) {
callback.setMsg("bean job , param[EXECUTOR_HANDLER] is null");
return JacksonUtil.writeValueAsString(callback);
}
handlerName = executor_handler;
} else {
// glue
String job_group = _param.get(HandlerParamEnum.JOB_GROUP.name());
String job_name = _param.get(HandlerParamEnum.JOB_NAME.name());
if (job_group==null || job_group.trim().length()==0 || job_name==null || job_name.trim().length()==0) {
callback.setMsg("glue job , param[JOB_GROUP or JOB_NAME] is null");
return JacksonUtil.writeValueAsString(callback);
}
}else{
callback.setMsg("param[HANDLER_NAME] can not be null.");
handlerName = "glue_".concat(job_group).concat("_").concat(job_name);
}
HandlerThread handlerThread = handlerTreadMap.get(handlerName);
if (handlerThread != null) {
IJobHandler handler = handlerThread.getHandler();
handlerThread.toStop();
handlerThread.interrupt();
regist(handlerName, handler);
callback.setStatus(RemoteCallBack.SUCCESS);
} else {
callback.setMsg("job handler[" + handlerName + "] not found.");
}
} else if (namespace.equals(ActionEnum.BEAT.name())) {
callback.setStatus(RemoteCallBack.SUCCESS);
callback.setMsg(null);
} else {
callback.setMsg("param[NAMESPACE] is not valid.");
callback.setMsg("param[Action] is not valid.");
return JacksonUtil.writeValueAsString(callback);
}
......
......@@ -11,6 +11,7 @@ import org.eclipse.jetty.util.ConcurrentHashSet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.xxl.job.core.handler.HandlerRepository.HandlerParamEnum;
import com.xxl.job.core.handler.IJobHandler.JobHandleStatus;
import com.xxl.job.core.log.XxlJobFileAppender;
import com.xxl.job.core.util.HttpUtil;
......@@ -47,7 +48,7 @@ public class HandlerThread extends Thread{
}
public void pushData(Map<String, String> param) {
if (param.get(HandlerRepository.TRIGGER_LOG_ID)!=null && !logIdSet.contains(param.get(HandlerRepository.TRIGGER_LOG_ID))) {
if (param.get(HandlerParamEnum.LOG_ID.name())!=null && !logIdSet.contains(param.get(HandlerParamEnum.LOG_ID.name()))) {
handlerDataQueue.offer(param);
}
}
......@@ -60,9 +61,9 @@ public class HandlerThread extends Thread{
Map<String, String> handlerData = handlerDataQueue.poll();
if (handlerData!=null) {
i= 0;
String trigger_log_address = handlerData.get(HandlerRepository.TRIGGER_LOG_ADDRESS);
String trigger_log_id = handlerData.get(HandlerRepository.TRIGGER_LOG_ID);
String handler_params = handlerData.get(HandlerRepository.HANDLER_PARAMS);
String trigger_log_address = handlerData.get(HandlerParamEnum.LOG_ADDRESS.name());
String trigger_log_id = handlerData.get(HandlerParamEnum.LOG_ID.name());
String handler_params = handlerData.get(HandlerParamEnum.EXECUTOR_PARAMS.name());
logIdSet.remove(trigger_log_id);
// parse param
......
package com.xxl.job.core.util;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
......@@ -98,9 +96,10 @@ public class HttpUtil {
}
} catch (Exception e) {
e.printStackTrace();
StringWriter out = new StringWriter();
/*StringWriter out = new StringWriter();
e.printStackTrace(new PrintWriter(out));
callback.setMsg(out.toString());
callback.setMsg(out.toString());*/
callback.setMsg(e.getMessage());
} finally{
if (httpPost!=null) {
httpPost.releaseConnection();
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册