Unverified 提交 e4c93174 authored 作者: 许雪里's avatar 许雪里 提交者: GitHub

Merge branch 'master' into master

# for eclipse
/.settings/
/.project
# for idea
.idea
.classpath
.project
*.iml
/.idea
*/target
target/
.DS_Store
.gitattributes
language: java
jdk:
- oraclejdk7
- oraclejdk8
install: mvn install -DskipTests=true -Dmaven.javadoc.skip=true
#script: mvn test
script: mvn -DskipTests=true clean package
\ No newline at end of file
差异被折叠。
差异被折叠。
CREATE database if NOT EXISTS `xxl-job` default character set utf8 collate utf8_general_ci;
use `xxl-job`;
CREATE TABLE XXL_JOB_QRTZ_JOB_DETAILS
(
......@@ -156,7 +160,7 @@ CREATE TABLE `XXL_JOB_QRTZ_TRIGGER_INFO` (
`alarm_email` varchar(255) DEFAULT NULL COMMENT '报警邮件',
`executor_route_strategy` varchar(50) DEFAULT NULL COMMENT '执行器路由策略',
`executor_handler` varchar(255) DEFAULT NULL COMMENT '执行器任务handler',
`executor_param` varchar(255) DEFAULT NULL COMMENT '执行器任务参数',
`executor_param` varchar(512) DEFAULT NULL COMMENT '执行器任务参数',
`executor_block_strategy` varchar(50) DEFAULT NULL COMMENT '阻塞处理策略',
`executor_fail_strategy` varchar(50) DEFAULT NULL COMMENT '失败处理策略',
`glue_type` varchar(50) NOT NULL COMMENT 'GLUE类型',
......@@ -174,7 +178,7 @@ CREATE TABLE `XXL_JOB_QRTZ_TRIGGER_LOG` (
`glue_type` varchar(50) DEFAULT NULL COMMENT 'GLUE类型',
`executor_address` varchar(255) DEFAULT NULL COMMENT '执行器地址,本次执行的地址',
`executor_handler` varchar(255) DEFAULT NULL COMMENT '执行器任务handler',
`executor_param` varchar(255) DEFAULT NULL COMMENT 'executor_param',
`executor_param` varchar(512) DEFAULT NULL COMMENT '执行器任务参数',
`trigger_time` datetime DEFAULT NULL COMMENT '调度-时间',
`trigger_code` varchar(255) NOT NULL DEFAULT '0' COMMENT '调度-结果',
`trigger_msg` varchar(2048) DEFAULT NULL COMMENT '调度-日志',
......@@ -210,7 +214,7 @@ CREATE TABLE `XXL_JOB_QRTZ_TRIGGER_GROUP` (
`title` varchar(12) NOT NULL COMMENT '执行器名称',
`order` tinyint(4) NOT NULL DEFAULT '0' COMMENT '排序',
`address_type` tinyint(4) NOT NULL DEFAULT '0' COMMENT '执行器地址类型:0=自动注册、1=手动录入',
`address_list` varchar(200) DEFAULT NULL COMMENT '执行器地址列表,多地址逗号分隔',
`address_list` varchar(512) DEFAULT NULL COMMENT '执行器地址列表,多地址逗号分隔',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
......
......@@ -3,7 +3,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>com.xuxueli</groupId>
<artifactId>xxl-job</artifactId>
<version>1.8.2-SNAPSHOT</version>
<version>1.9.0-SNAPSHOT</version>
<packaging>pom</packaging>
<name>${project.artifactId}</name>
......@@ -20,31 +20,32 @@
<javax.servlet-api.version>3.0.1</javax.servlet-api.version>
<jsp-api.version>2.2</jsp-api.version>
<spring.version>3.2.18.RELEASE</spring.version>
<jackson-mapper-asl.version>1.9.13</jackson-mapper-asl.version>
<aspectjweaver.version>1.8.7</aspectjweaver.version>
<spring.version>4.3.13.RELEASE</spring.version>
<jackson.version>2.9.3</jackson.version>
<aspectjweaver.version>1.8.13</aspectjweaver.version>
<slf4j-api.version>1.7.25</slf4j-api.version>
<freemarker.version>2.3.20</freemarker.version>
<junit.version>4.11</junit.version>
<freemarker.version>2.3.23</freemarker.version>
<junit.version>4.12</junit.version>
<jetty-server.version>9.2.22.v20170606</jetty-server.version>
<hessian.version>4.0.51</hessian.version>
<httpclient.version>4.5.4</httpclient.version>
<jetty-server.version>9.4.6.v20170531</jetty-server.version>
<hessian.version>4.0.38</hessian.version>
<httpclient.version>4.3.6</httpclient.version>
<commons-exec.version>1.3</commons-exec.version>
<commons-beanutils.version>1.9.2</commons-beanutils.version>
<commons-lang.version>2.6</commons-lang.version>
<commons-collections4.version>4.1</commons-collections4.version>
<commons-lang3.version>3.7</commons-lang3.version>
<commons-email.version>1.5</commons-email.version>
<c3p0.version>0.9.5.2</c3p0.version>
<mysql-connector-java.version>5.1.29</mysql-connector-java.version>
<mybatis-spring.version>1.2.2</mybatis-spring.version>
<mybatis.version>3.2.8</mybatis.version>
<mysql-connector-java.version>5.1.45</mysql-connector-java.version>
<mybatis-spring.version>1.3.1</mybatis-spring.version>
<mybatis.version>3.4.5</mybatis.version>
<groovy-all.version>2.4.5</groovy-all.version>
<mail.version>1.4.6</mail.version>
<groovy-all.version>2.4.13</groovy-all.version>
<quartz.version>2.3.0</quartz.version>
<spring-boot.version>1.5.6.RELEASE</spring-boot.version>
<spring-boot.version>1.5.9.RELEASE</spring-boot.version>
</properties>
<build>
......
......@@ -4,7 +4,7 @@
<parent>
<groupId>com.xuxueli</groupId>
<artifactId>xxl-job</artifactId>
<version>1.8.2-SNAPSHOT</version>
<version>1.9.0-SNAPSHOT</version>
</parent>
<artifactId>xxl-job-admin</artifactId>
<packaging>war</packaging>
......@@ -40,18 +40,22 @@
</dependency>
<!-- jackson (support spring json) -->
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>${jackson-mapper-asl.version}</version>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
</dependency>
<!-- slf4j -->
<!-- servlet -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>${slf4j-api.version}</version>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>${javax.servlet-api.version}</version>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>${jsp-api.version}</version>
</dependency>
<!-- freemarker -->
<dependency>
<groupId>org.freemarker</groupId>
......@@ -59,37 +63,37 @@
<version>${freemarker.version}</version>
</dependency>
<!-- commons-beanutils -->
<!-- slf4j -->
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
<version>${commons-beanutils.version}</version>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>${slf4j-api.version}</version>
</dependency>
<!-- commons-lang -->
<!-- junit -->
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>${commons-lang.version}</version>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<!-- servlet -->
<!-- commons-collections4 -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>${javax.servlet-api.version}</version>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>${commons-collections4.version}</version>
</dependency>
<!-- commons-lang3 -->
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>${jsp-api.version}</version>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>${commons-lang3.version}</version>
</dependency>
<!-- junit -->
<!-- commons-email -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
<groupId>org.apache.commons</groupId>
<artifactId>commons-email</artifactId>
<version>${commons-email.version}</version>
</dependency>
<!-- c3p0 -->
......@@ -116,7 +120,6 @@
<version>${mybatis.version}</version>
</dependency>
<!-- httpclient -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
......@@ -124,13 +127,6 @@
<version>${httpclient.version}</version>
</dependency>
<!-- javax.mail -->
<dependency>
<groupId>javax.mail</groupId>
<artifactId>mail</artifactId>
<version>${mail.version}</version>
</dependency>
<!-- quartz :quartz-2.2.3/c3p0-0.9.1.1/slf4j-api-1.6.6 -->
<dependency>
<groupId>org.quartz-scheduler</groupId>
......
......@@ -5,9 +5,12 @@ import com.xxl.job.admin.controller.interceptor.PermissionInterceptor;
import com.xxl.job.admin.core.util.PropertiesUtil;
import com.xxl.job.admin.service.XxlJobService;
import com.xxl.job.core.biz.model.ReturnT;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.propertyeditors.CustomDateEditor;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
......@@ -15,6 +18,8 @@ import org.springframework.web.bind.annotation.ResponseBody;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;
/**
......@@ -38,8 +43,8 @@ public class IndexController {
@RequestMapping("/triggerChartDate")
@ResponseBody
public ReturnT<Map<String, Object>> triggerChartDate() {
ReturnT<Map<String, Object>> triggerChartDate = xxlJobService.triggerChartDate();
public ReturnT<Map<String, Object>> triggerChartDate(Date startDate, Date endDate) {
ReturnT<Map<String, Object>> triggerChartDate = xxlJobService.triggerChartDate(startDate, endDate);
return triggerChartDate;
}
......@@ -91,5 +96,12 @@ public class IndexController {
return "help";
}
@InitBinder
public void initBinder(WebDataBinder binder) {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
dateFormat.setLenient(false);
binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, true));
}
}
......@@ -4,7 +4,7 @@ import com.xxl.job.admin.core.model.XxlJobGroup;
import com.xxl.job.admin.dao.XxlJobGroupDao;
import com.xxl.job.admin.dao.XxlJobInfoDao;
import com.xxl.job.core.biz.model.ReturnT;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
......
......@@ -11,8 +11,8 @@ import com.xxl.job.core.biz.ExecutorBiz;
import com.xxl.job.core.biz.model.LogResult;
import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.rpc.netcom.NetComClientProxy;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.time.DateUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
......
package com.xxl.job.admin.controller.interceptor;
import java.util.HashMap;
import org.apache.commons.lang3.ArrayUtils;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang.ArrayUtils;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import java.util.HashMap;
/**
* push cookies to model as cookieMap
*
* @author xuxueli 2015-12-12 18:09:04
*/
public class CookieInterceptor extends HandlerInterceptorAdapter {
......
......@@ -12,11 +12,12 @@ import java.math.BigInteger;
/**
* 权限拦截, 简易版
*
* @author xuxueli 2015-12-12 18:09:04
*/
public class PermissionInterceptor extends HandlerInterceptorAdapter {
public static final String LOGIN_IDENTITY_KEY = "LOGIN_IDENTITY";
public static final String LOGIN_IDENTITY_KEY = "XXL_JOB_LOGIN_IDENTITY";
public static final String LOGIN_IDENTITY_TOKEN;
static {
String username = PropertiesUtil.getString("xxl.job.login.username");
......
package com.xxl.job.admin.core.jobbean;
//package com.xxl.job.action.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.HandlerRouter;
//import com.xxl.job.client.util.XxlJobNetCommUtil.RemoteCallBack;
//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.thread.JobFailMonitorHelper;
//import com.xxl.job.core.util.DynamicSchedulerUtil;
//
///**
// * http job bean
// * @author xuxueli 2015-12-17 18:20:34
// */
//@Deprecated
//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(HandlerRouter.HANDLER_PARAMS);
// String[] handlerParams = null;
// if (StringUtils.isNotBlank(handler_params)) {
// handlerParams = handler_params.split(",");
// }
//
// jobLog.setTriggerTime(new Date());
// jobLog.setTriggerStatus(RemoteCallBack.SUCCESS);
// jobLog.setTriggerMsg(null);
//
// try {
// Object responseMsg = this.handle(handlerParams);
//
// jobLog.setHandleTime(new Date());
// jobLog.setHandleStatus(RemoteCallBack.SUCCESS);
// jobLog.setHandleMsg(JacksonUtil.writeValueAsString(responseMsg));
// } catch (Exception e) {
// logger.info("JobThread Exception:", e);
// StringWriter out = new StringWriter();
// e.printStackTrace(new PrintWriter(out));
//
// jobLog.setHandleTime(new Date());
// jobLog.setHandleStatus(RemoteCallBack.FAIL);
// jobLog.setHandleMsg(out.toString());
// }
//
// // update trigger info
// DynamicSchedulerUtil.xxlJobLogDao.updateTriggerInfo(jobLog);
// DynamicSchedulerUtil.xxlJobLogDao.updateHandleInfo(jobLog);
// JobFailMonitorHelper.monitor(jobLog.getId());
// logger.info(">>>>>>>>>>> xxl-job trigger end, jobLog.id:{}, jobLog:{}", jobLog.getId(), jobLog);
//
// }
//
// public abstract Object handle(String... param);
//
//}
\ No newline at end of file
package com.xxl.job.admin.core.jobbean.impl;
//package com.xxl.job.action.job.impl;
//
//import java.util.concurrent.TimeUnit;
//
//import org.quartz.DisallowConcurrentExecution;
//
//import com.xxl.job.action.job.LocalNomalJobBean;
//
///**
// * demo job bean for no-concurrent
// * @author xuxueli 2016-3-12 14:25:14
// */
//@Deprecated
//@DisallowConcurrentExecution // 串行;线程数要多配置几个,否则不生效;
//public class DemoConcurrentJobBean extends LocalNomalJobBean {
//
// @Override
// public Object handle(String... param) {
//
// try {
// TimeUnit.SECONDS.sleep(10);
// } catch (InterruptedException e) {
// logger.error(e.getMessage(), e);
// }
//
// return false;
// }
//
//}
package com.xxl.job.admin.core.jobbean.impl;
//package com.xxl.job.action.job.impl;
//
//import java.util.concurrent.TimeUnit;
//
//import org.slf4j.Logger;
//import org.slf4j.LoggerFactory;
//
//import com.xxl.job.action.job.LocalNomalJobBean;
//
///**
// * demo job bean for concurrent
// * @author xuxueli 2016-3-12 14:25:57
// */
//@Deprecated
//public class DemoNomalJobBean extends LocalNomalJobBean {
// private static Logger Logger = LoggerFactory.getLogger(DemoNomalJobBean.class);
//
// @Override
// public Object handle(String... param) {
// Logger.info("DemoNomalJobBean run :" + param);
//
// try {
// TimeUnit.SECONDS.sleep(10);
// } catch (InterruptedException e) {
// logger.error(e.getMessage(), e);
// }
//
// return false;
// }
//
//}
package com.xxl.job.admin.core.model;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.StringUtils;
import java.util.ArrayList;
import java.util.Arrays;
......
......@@ -4,8 +4,11 @@ import com.xxl.job.admin.core.model.XxlJobGroup;
import com.xxl.job.admin.core.model.XxlJobInfo;
import com.xxl.job.admin.core.model.XxlJobLog;
import com.xxl.job.admin.core.schedule.XxlJobDynamicScheduler;
import com.xxl.job.admin.core.util.JobKeyUtil;
import com.xxl.job.admin.core.util.MailUtil;
import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.handler.IJobHandler;
import org.apache.commons.collections4.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
......@@ -26,6 +29,8 @@ public class JobFailMonitorHelper {
return instance;
}
// ---------------------- monitor ----------------------
private LinkedBlockingQueue<Integer> queue = new LinkedBlockingQueue<Integer>(0xfff8);
private Thread monitorThread;
......@@ -35,33 +40,41 @@ public class JobFailMonitorHelper {
@Override
public void run() {
// monitor
while (!toStop) {
try {
Integer jobLogId = JobFailMonitorHelper.instance.queue.take();
if (jobLogId != null && jobLogId > 0) {
XxlJobLog log = XxlJobDynamicScheduler.xxlJobLogDao.load(jobLogId);
if (log!=null) {
if (ReturnT.SUCCESS_CODE==log.getTriggerCode() && log.getHandleCode()==0) {
// job running, wait + again monitor
TimeUnit.SECONDS.sleep(10);
List<Integer> jobLogIdList = new ArrayList<Integer>();
int drainToNum = JobFailMonitorHelper.instance.queue.drainTo(jobLogIdList);
if (CollectionUtils.isNotEmpty(jobLogIdList)) {
for (Integer jobLogId : jobLogIdList) {
if (jobLogId==null || jobLogId==0) {
continue;
}
XxlJobLog log = XxlJobDynamicScheduler.xxlJobLogDao.load(jobLogId);
if (log == null) {
continue;
}
if (IJobHandler.SUCCESS.getCode() == log.getTriggerCode() && log.getHandleCode() == 0) {
JobFailMonitorHelper.monitor(jobLogId);
logger.info(">>>>>>>>>>> job monitor, job running, JobLogId:{}", jobLogId);
}
if (ReturnT.SUCCESS_CODE==log.getTriggerCode() && ReturnT.SUCCESS_CODE==log.getHandleCode()) {
} else if (IJobHandler.SUCCESS.getCode() == log.getHandleCode()) {
// job success, pass
logger.info(">>>>>>>>>>> job monitor, job success, JobLogId:{}", jobLogId);
}
if (ReturnT.FAIL_CODE == log.getTriggerCode()|| ReturnT.FAIL_CODE==log.getHandleCode()) {
} else if (IJobHandler.FAIL.getCode() == log.getTriggerCode()
|| IJobHandler.FAIL.getCode() == log.getHandleCode()
|| IJobHandler.FAIL_RETRY.getCode() == log.getHandleCode() ) {
// job fail,
sendMonitorEmail(log);
failAlarm(log);
logger.info(">>>>>>>>>>> job monitor, job fail, JobLogId:{}", jobLogId);
} else {
JobFailMonitorHelper.monitor(jobLogId);
logger.info(">>>>>>>>>>> job monitor, job status unknown, JobLogId:{}", jobLogId);
}
}
}
TimeUnit.SECONDS.sleep(10);
} catch (Exception e) {
logger.error("job monitor error:{}", e);
}
......@@ -75,7 +88,7 @@ public class JobFailMonitorHelper {
XxlJobLog log = XxlJobDynamicScheduler.xxlJobLogDao.load(jobLogId);
if (ReturnT.FAIL_CODE == log.getTriggerCode()|| ReturnT.FAIL_CODE==log.getHandleCode()) {
// job fail,
sendMonitorEmail(log);
failAlarm(log);
logger.info(">>>>>>>>>>> job monitor last, job fail, JobLogId:{}", jobLogId);
}
}
......@@ -87,24 +100,6 @@ public class JobFailMonitorHelper {
monitorThread.start();
}
/**
* send monitor email
* @param jobLog
*/
private void sendMonitorEmail(XxlJobLog jobLog){
XxlJobInfo info = XxlJobDynamicScheduler.xxlJobInfoDao.loadById(jobLog.getJobId());
if (info!=null && info.getAlarmEmail()!=null && info.getAlarmEmail().trim().length()>0) {
Set<String> emailSet = new HashSet<String>(Arrays.asList(info.getAlarmEmail().split(",")));
for (String email: emailSet) {
String title = "《调度监控报警》(任务调度中心XXL-JOB)";
XxlJobGroup group = XxlJobDynamicScheduler.xxlJobGroupDao.load(Integer.valueOf(info.getJobGroup()));
String content = MessageFormat.format("任务调度失败, 执行器名称:{0}, 任务描述:{1}.", group!=null?group.getTitle():"null", info.getJobDesc());
MailUtil.sendMail(email, title, content, false, null);
}
}
}
public void toStop(){
toStop = true;
// interrupt and wait
......@@ -120,5 +115,55 @@ public class JobFailMonitorHelper {
public static void monitor(int jobLogId){
getInstance().queue.offer(jobLogId);
}
// ---------------------- alarm ----------------------
// email alarm template
private static final String mailBodyTemplate = "<h5>监控告警明细:</span>" +
"<table border=\"1\" cellpadding=\"3\" style=\"border-collapse:collapse; width:80%;\" >\n" +
" <thead style=\"font-weight: bold;color: #ffffff;background-color: #ff8c00;\" >" +
" <tr>\n" +
" <td>执行器</td>\n" +
" <td>JobKey</td>\n" +
" <td>任务描述</td>\n" +
" <td>告警类型</td>\n" +
" </tr>\n" +
" <thead/>\n" +
" <tbody>\n" +
" <tr>\n" +
" <td>{0}</td>\n" +
" <td>{1}</td>\n" +
" <td>{2}</td>\n" +
" <td>调度失败</td>\n" +
" </tr>\n" +
" <tbody>\n" +
"</table>";
/**
* fail alarm
*
* @param jobLog
*/
private void failAlarm(XxlJobLog jobLog){
// send monitor email
XxlJobInfo info = XxlJobDynamicScheduler.xxlJobInfoDao.loadById(jobLog.getJobId());
if (info!=null && info.getAlarmEmail()!=null && info.getAlarmEmail().trim().length()>0) {
Set<String> emailSet = new HashSet<String>(Arrays.asList(info.getAlarmEmail().split(",")));
for (String email: emailSet) {
XxlJobGroup group = XxlJobDynamicScheduler.xxlJobGroupDao.load(Integer.valueOf(info.getJobGroup()));
String title = "调度中心监控报警";
String content = MessageFormat.format(mailBodyTemplate, group!=null?group.getTitle():"null", JobKeyUtil.formatJobKey(info), info.getJobDesc());
MailUtil.sendMail(email, title, content);
}
}
// TODO, custom alarm strategy, such as sms
}
}
......@@ -4,8 +4,8 @@ import com.xxl.job.admin.core.model.XxlJobGroup;
import com.xxl.job.admin.core.model.XxlJobRegistry;
import com.xxl.job.admin.core.schedule.XxlJobDynamicScheduler;
import com.xxl.job.core.enums.RegistryConfig;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
......
package com.xxl.job.admin.core.util;
import com.xxl.job.admin.core.model.XxlJobInfo;
import org.apache.commons.lang3.StringUtils;
/**
* job key util
*
* @author xuxueli 2017-12-22 18:48:45
*/
public class JobKeyUtil {
/**
* format job key
*
* @param xxlJobInfo
* @return
*/
public static String formatJobKey(XxlJobInfo xxlJobInfo){
return String.valueOf(xxlJobInfo.getJobGroup())
.concat("_").concat(String.valueOf(xxlJobInfo.getId()));
}
/**
* parse jobId from JobKey
*
* @param jobKey
* @return
*/
public static int parseJobId(String jobKey){
if (jobKey!=null && jobKey.trim().length()>0) {
String[] jobKeyArr = jobKey.split("_");
if (jobKeyArr.length == 2) {
String jobIdStr = jobKeyArr[1];
if (StringUtils.isNotBlank(jobIdStr) && StringUtils.isNumeric(jobIdStr)) {
int jobId = Integer.valueOf(jobIdStr);
return jobId;
}
}
}
return -1;
}
}
package com.xxl.job.admin.core.util;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.mail.DefaultAuthenticator;
import org.apache.commons.mail.EmailException;
import org.apache.commons.mail.HtmlEmail;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.JavaMailSenderImpl;
import org.springframework.mail.javamail.MimeMessageHelper;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeUtility;
import java.io.File;
import java.util.Properties;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.nio.charset.Charset;
/**
* 邮件发送.Util
*
* @author xuxueli 2016-3-12 15:06:20
*/
public class MailUtil {
......@@ -25,156 +20,52 @@ public class MailUtil {
private static String port;
private static String username;
private static String password;
private static String sendFrom;
private static String sendNick;
static{
host = PropertiesUtil.getString("xxl.job.mail.host");
port = PropertiesUtil.getString("xxl.job.mail.port");
username = PropertiesUtil.getString("xxl.job.mail.username");
password = PropertiesUtil.getString("xxl.job.mail.password");
sendFrom = PropertiesUtil.getString("xxl.job.mail.sendFrom");
sendNick = PropertiesUtil.getString("xxl.job.mail.sendNick");
}
/**
<!-- spring mail sender -->
<bean id="javaMailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl" scope="singleton" >
<property name="host" value="${mail.host}" /> <!-- SMTP发送邮件的服务器的IP和端口 -->
<property name="port" value="${mail.port}" />
<property name="username" value="${mail.username}" /> <!-- 登录SMTP邮件发送服务器的用户名和密码 -->
<property name="password" value="${mail.password}" />
<property name="javaMailProperties"> <!-- 获得邮件会话属性,验证登录邮件服务器是否成功 -->
<props>
<prop key="mail.smtp.auth">true</prop>
<prop key="prop">true</prop>
<!-- <prop key="mail.smtp.timeout">25000</prop> -->
</props>
</property>
</bean>
*/
/**
* 发送邮件 (完整版)(结合Spring)
*
* //@param javaMailSender: 发送Bean
* //@param sendFrom : 发送人邮箱
* //@param sendNick : 发送人昵称
* @param toAddress : 收件人邮箱
* @param mailSubject : 邮件主题
* @param mailBody : 邮件正文
* @param mailBodyIsHtml: 邮件正文格式,true:HTML格式;false:文本格式
* @param attachments : 附件
*
* @param toAddress 收件人邮箱
* @param mailSubject 邮件主题
* @param mailBody 邮件正文
* @return
*/
@SuppressWarnings("null")
public static boolean sendMailSpring(String toAddress, String mailSubject, String mailBody, boolean mailBodyIsHtml,File[] attachments) {
JavaMailSender javaMailSender = null;//ResourceBundle.getInstance().getJavaMailSender();
public static boolean sendMail(String toAddress, String mailSubject, String mailBody){
try {
MimeMessage mimeMessage = javaMailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, ArrayUtils.isNotEmpty(attachments), "UTF-8"); // 设置utf-8或GBK编码,否则邮件会有乱码;multipart,true表示文件上传
helper.setFrom(sendFrom, sendNick);
helper.setTo(toAddress);
// Create the email message
HtmlEmail email = new HtmlEmail();
// 设置收件人抄送的名片和地址(相当于群发了)
//helper.setCc(InternetAddress.parse(MimeUtility.encodeText("邮箱001") + " <@163.com>," + MimeUtility.encodeText("邮箱002") + " <@foxmail.com>"));
//email.setDebug(true); // 将会打印一些log
//email.setTLS(true); // 是否TLS校验,,某些邮箱需要TLS安全校验,同理有SSL校验
//email.setSSL(true);
helper.setSubject(mailSubject);
helper.setText(mailBody, mailBodyIsHtml);
// 添加附件
if (ArrayUtils.isNotEmpty(attachments)) {
for (File file : attachments) {
helper.addAttachment(MimeUtility.encodeText(file.getName()), file);
}
}
// 群发
//MimeMessage[] mailMessages = { mimeMessage };
javaMailSender.send(mimeMessage);
return true;
} catch (Exception e) {
logger.info("{}", e);
}
return false;
}
/**
* 发送邮件 (完整版) (纯JavaMail)
*
* @param toAddress : 收件人邮箱
* @param mailSubject : 邮件主题
* @param mailBody : 邮件正文
* @param mailBodyIsHtml: 邮件正文格式,true:HTML格式;false:文本格式
* //@param inLineFile : 内嵌文件
* @param attachments : 附件
*/
public static boolean sendMail (String toAddress, String mailSubject, String mailBody,
boolean mailBodyIsHtml, File[] attachments){
try {
// 创建邮件发送类 JavaMailSender (用于发送多元化邮件,包括附件,图片,html 等 )
JavaMailSenderImpl mailSender = new JavaMailSenderImpl();
mailSender.setHost(host); // 设置邮件服务主机
mailSender.setUsername(username); // 发送者邮箱的用户名
mailSender.setPassword(password); // 发送者邮箱的密码
//配置文件,用于实例化java.mail.session
Properties pro = new Properties();
pro.put("mail.smtp.auth", "true"); // 登录SMTP服务器,需要获得授权 (网易163邮箱新近注册的邮箱均不能授权,测试 sohu 的邮箱可以获得授权)
pro.put("mail.smtp.socketFactory.port", port);
pro.put("mail.smtp.socketFactory.fallback", "false");
mailSender.setJavaMailProperties(pro);
//创建多元化邮件 (创建 mimeMessage 帮助类,用于封装信息至 mimeMessage)
MimeMessage mimeMessage = mailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, ArrayUtils.isNotEmpty(attachments), "UTF-8");
helper.setFrom(sendFrom, sendNick);
helper.setTo(toAddress);
email.setHostName(host);
email.setSmtpPort(Integer.valueOf(port));
//email.setSslSmtpPort(port);
email.setAuthenticator(new DefaultAuthenticator(username, password));
email.setCharset(Charset.defaultCharset().name());
email.setFrom(username, sendNick);
email.addTo(toAddress);
email.setSubject(mailSubject);
email.setMsg(mailBody);
helper.setSubject(mailSubject);
helper.setText(mailBody, mailBodyIsHtml);
// 添加内嵌文件,第1个参数为cid标识这个文件,第2个参数为资源
//helper.addInline(MimeUtility.encodeText(inLineFile.getName()), inLineFile);
// 添加附件
if (ArrayUtils.isNotEmpty(attachments)) {
for (File file : attachments) {
helper.addAttachment(MimeUtility.encodeText(file.getName()), file);
}
}
mailSender.send(mimeMessage);
//email.attach(attachment); // add the attachment
email.send(); // send the email
return true;
} catch (Exception e) {
} catch (EmailException e) {
logger.error(e.getMessage(), e);
}
return false;
}
static int total = 0;
public static void main(String[] args) {
ExecutorService exec = Executors.newCachedThreadPool();
for (int i = 0; i < 20; i++) {
exec.execute(new Thread(new Runnable() {
@Override
public void run() {
while(total < 10){
String mailBody = "<html><head><meta http-equiv="
+ "Content-Type"
+ " content="
+ "text/html; charset=gb2312"
+ "></head><body><h1>新书快递通知</h1>你的新书快递申请已推送新书,请到<a href=''>空间"
+ "</a>中查看</body></html>";
sendMail("ovono802302@163.com", "测试邮件", mailBody, false, null);
System.out.println(total);
total++;
}
}
}));
}
}
}
......@@ -12,6 +12,7 @@ import java.util.Properties;
/**
* properties util
*
* @author xuxueli 2015-8-28 10:35:53
*/
public class PropertiesUtil {
......@@ -33,9 +34,5 @@ public class PropertiesUtil {
}
return null;
}
public static void main(String[] args) {
System.out.println(getString("xxl.job.login.username"));
}
}
......@@ -4,6 +4,7 @@ package com.xxl.job.admin.service;
import com.xxl.job.admin.core.model.XxlJobInfo;
import com.xxl.job.core.biz.model.ReturnT;
import java.util.Date;
import java.util.Map;
/**
......@@ -29,6 +30,6 @@ public interface XxlJobService {
public Map<String,Object> dashboardInfo();
public ReturnT<Map<String,Object>> triggerChartDate();
public ReturnT<Map<String,Object>> triggerChartDate(Date startDate, Date endDate);
}
......@@ -5,6 +5,7 @@ import com.xxl.job.admin.core.model.XxlJobInfo;
import com.xxl.job.admin.core.model.XxlJobLog;
import com.xxl.job.admin.core.schedule.XxlJobDynamicScheduler;
import com.xxl.job.admin.core.trigger.XxlJobTrigger;
import com.xxl.job.admin.core.util.JobKeyUtil;
import com.xxl.job.admin.dao.XxlJobInfoDao;
import com.xxl.job.admin.dao.XxlJobLogDao;
import com.xxl.job.admin.dao.XxlJobRegistryDao;
......@@ -13,7 +14,8 @@ import com.xxl.job.core.biz.AdminBiz;
import com.xxl.job.core.biz.model.HandleCallbackParam;
import com.xxl.job.core.biz.model.RegistryParam;
import com.xxl.job.core.biz.model.ReturnT;
import org.apache.commons.lang.StringUtils;
import com.xxl.job.core.handler.IJobHandler;
import org.apache.commons.lang3.StringUtils;
import org.quartz.SchedulerException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
......@@ -45,8 +47,8 @@ public class AdminBizImpl implements AdminBiz {
public ReturnT<String> callback(List<HandleCallbackParam> callbackParamList) {
for (HandleCallbackParam handleCallbackParam: callbackParamList) {
ReturnT<String> callbackResult = callback(handleCallbackParam);
logger.info("JobApiController.callback {}, handleCallbackParam={}, callbackResult={}",
(callbackResult.getCode()==ReturnT.SUCCESS_CODE?"success":"fail"), handleCallbackParam, callbackResult);
logger.info(">>>>>>>>> JobApiController.callback {}, handleCallbackParam={}, callbackResult={}",
(callbackResult.getCode()==IJobHandler.SUCCESS.getCode()?"success":"fail"), handleCallbackParam, callbackResult);
}
return ReturnT.SUCCESS;
......@@ -58,28 +60,39 @@ public class AdminBizImpl implements AdminBiz {
if (log == null) {
return new ReturnT<String>(ReturnT.FAIL_CODE, "log item not found.");
}
if (log.getHandleCode() > 0) {
return new ReturnT<String>(ReturnT.FAIL_CODE, "log repeate callback."); // avoid repeat callback, trigger child job etc
}
// trigger success, to trigger child job, and avoid repeat trigger child job
String childTriggerMsg = null;
if (ReturnT.SUCCESS_CODE==handleCallbackParam.getExecuteResult().getCode() && ReturnT.SUCCESS_CODE!=log.getHandleCode()) {
// trigger success, to trigger child job
String callbackMsg = null;
if (IJobHandler.SUCCESS.getCode() == handleCallbackParam.getExecuteResult().getCode()) {
XxlJobInfo xxlJobInfo = xxlJobInfoDao.loadById(log.getJobId());
if (xxlJobInfo!=null && StringUtils.isNotBlank(xxlJobInfo.getChildJobKey())) {
childTriggerMsg = "<hr>";
callbackMsg = "<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发子任务<<<<<<<<<<< </span><br>";
String[] childJobKeys = xxlJobInfo.getChildJobKey().split(",");
for (int i = 0; i < childJobKeys.length; i++) {
String[] jobKeyArr = childJobKeys[i].split("_");
if (jobKeyArr!=null && jobKeyArr.length==2) {
ReturnT<String> triggerChildResult = xxlJobService.triggerJob(Integer.valueOf(jobKeyArr[1]));
int childJobId = JobKeyUtil.parseJobId(childJobKeys[i]);
if (childJobId > 0) {
ReturnT<String> triggerChildResult = xxlJobService.triggerJob(childJobId);
// add msg
childTriggerMsg += MessageFormat.format("<br> {0}/{1} 触发子任务{2}, 子任务Key: {3}, 子任务触发备注: {4}",
(i+1), childJobKeys.length, (triggerChildResult.getCode()==ReturnT.SUCCESS_CODE?"成功":"失败"), childJobKeys[i], triggerChildResult.getMsg());
callbackMsg += MessageFormat.format("{0}/{1} [JobKey={2}], 触发{3}, 触发备注: {4} <br>",
(i+1), childJobKeys.length, childJobKeys[i], (triggerChildResult.getCode()==ReturnT.SUCCESS_CODE?"成功":"失败"), triggerChildResult.getMsg());
} else {
childTriggerMsg += MessageFormat.format("<br> {0}/{1} 触发子任务失败, 子任务Key格式错误, 子任务Key: {2}",
callbackMsg += MessageFormat.format(" {0}/{1} [JobKey={2}], 触发失败, 触发备注: JobKey格式错误 <br>",
(i+1), childJobKeys.length, childJobKeys[i]);
}
}
}
} else if (IJobHandler.FAIL_RETRY.getCode() == handleCallbackParam.getExecuteResult().getCode()){
ReturnT<String> retryTriggerResult = xxlJobService.triggerJob(log.getJobId());
callbackMsg = "<br><br><span style=\"color:#F39C12;\" > >>>>>>>>>>>执行失败重试<<<<<<<<<<< </span><br>";
callbackMsg += MessageFormat.format("触发{0}, 触发备注: {1}",
(retryTriggerResult.getCode()==ReturnT.SUCCESS_CODE?"成功":"失败"), retryTriggerResult.getMsg());
}
// handle msg
......@@ -90,8 +103,8 @@ public class AdminBizImpl implements AdminBiz {
if (handleCallbackParam.getExecuteResult().getMsg() != null) {
handleMsg.append(handleCallbackParam.getExecuteResult().getMsg());
}
if (childTriggerMsg !=null) {
handleMsg.append("<br>子任务触发备注:").append(childTriggerMsg);
if (callbackMsg != null) {
handleMsg.append(callbackMsg);
}
// success, save log
......
......@@ -5,6 +5,7 @@ import com.xxl.job.admin.core.model.XxlJobGroup;
import com.xxl.job.admin.core.model.XxlJobInfo;
import com.xxl.job.admin.core.route.ExecutorRouteStrategyEnum;
import com.xxl.job.admin.core.schedule.XxlJobDynamicScheduler;
import com.xxl.job.admin.core.util.JobKeyUtil;
import com.xxl.job.admin.dao.XxlJobGroupDao;
import com.xxl.job.admin.dao.XxlJobInfoDao;
import com.xxl.job.admin.dao.XxlJobLogDao;
......@@ -13,10 +14,10 @@ import com.xxl.job.admin.service.XxlJobService;
import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.enums.ExecutorBlockStrategyEnum;
import com.xxl.job.core.glue.GlueTypeEnum;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.time.DateUtils;
import org.apache.commons.lang.time.FastDateFormat;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.apache.commons.lang3.time.FastDateFormat;
import org.quartz.CronExpression;
import org.quartz.SchedulerException;
import org.slf4j.Logger;
......@@ -107,11 +108,11 @@ public class XxlJobServiceImpl implements XxlJobService {
if (StringUtils.isNotBlank(jobInfo.getChildJobKey())) {
String[] childJobKeys = jobInfo.getChildJobKey().split(",");
for (String childJobKeyItem: childJobKeys) {
String[] childJobKeyArr = childJobKeyItem.split("_");
if (childJobKeyArr.length!=2) {
int childJobId = JobKeyUtil.parseJobId(childJobKeyItem);
if (childJobId <= 0) {
return new ReturnT<String>(ReturnT.FAIL_CODE, MessageFormat.format("子任务Key({0})格式错误", childJobKeyItem));
}
XxlJobInfo childJobInfo = xxlJobInfoDao.loadById(Integer.valueOf(childJobKeyArr[1]));
XxlJobInfo childJobInfo = xxlJobInfoDao.loadById(childJobId);
if (childJobInfo==null) {
return new ReturnT<String>(ReturnT.FAIL_CODE, MessageFormat.format("子任务Key({0})无效", childJobKeyItem));
}
......@@ -170,11 +171,11 @@ public class XxlJobServiceImpl implements XxlJobService {
if (StringUtils.isNotBlank(jobInfo.getChildJobKey())) {
String[] childJobKeys = jobInfo.getChildJobKey().split(",");
for (String childJobKeyItem: childJobKeys) {
String[] childJobKeyArr = childJobKeyItem.split("_");
if (childJobKeyArr.length!=2) {
int childJobId = JobKeyUtil.parseJobId(childJobKeyItem);
if (childJobId <= 0) {
return new ReturnT<String>(ReturnT.FAIL_CODE, MessageFormat.format("子任务Key({0})格式错误", childJobKeyItem));
}
XxlJobInfo childJobInfo = xxlJobInfoDao.loadById(Integer.valueOf(childJobKeyArr[1]));
XxlJobInfo childJobInfo = xxlJobInfoDao.loadById(childJobId);
if (childJobInfo==null) {
return new ReturnT<String>(ReturnT.FAIL_CODE, MessageFormat.format("子任务Key({0})无效", childJobKeyItem));
}
......@@ -310,18 +311,15 @@ public class XxlJobServiceImpl implements XxlJobService {
}
@Override
public ReturnT<Map<String, Object>> triggerChartDate() {
Date from = DateUtils.addDays(new Date(), -30);
Date to = new Date();
public ReturnT<Map<String, Object>> triggerChartDate(Date startDate, Date endDate) {
List<String> triggerDayList = new ArrayList<String>();
List<Integer> triggerDayCountSucList = new ArrayList<Integer>();
List<Integer> triggerDayCountFailList = new ArrayList<Integer>();
int triggerCountSucTotal = 0;
int triggerCountFailTotal = 0;
List<Map<String, Object>> triggerCountMapAll = xxlJobLogDao.triggerCountByDay(from, to, -1);
List<Map<String, Object>> triggerCountMapSuc = xxlJobLogDao.triggerCountByDay(from, to, ReturnT.SUCCESS_CODE);
List<Map<String, Object>> triggerCountMapAll = xxlJobLogDao.triggerCountByDay(startDate, endDate, -1);
List<Map<String, Object>> triggerCountMapSuc = xxlJobLogDao.triggerCountByDay(startDate, endDate, ReturnT.SUCCESS_CODE);
if (CollectionUtils.isNotEmpty(triggerCountMapAll)) {
for (Map<String, Object> item: triggerCountMapAll) {
String day = String.valueOf(item.get("triggerDay"));
......
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=/data/applogs/xxl-job/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
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration PUBLIC "-//log4j/log4j Configuration//EN" "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/" threshold="null" debug="null">
<appender name="CONSOLE" class="org.apache.log4j.ConsoleAppender">
<param name="Target" value="System.out" />
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%-d{yyyy-MM-dd HH:mm:ss} xxl-job-admin [%c]-[%t]-[%M]-[%L]-[%p] %m%n"/>
</layout>
</appender>
<appender name="FILE" class="org.apache.log4j.DailyRollingFileAppender">
<param name="file" value="/data/applogs/xxl-job/xxl-job-admin.log"/>
<param name="append" value="true"/>
<param name="encoding" value="UTF-8"/>
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%-d{yyyy-MM-dd HH:mm:ss} xxl-job-admin [%c]-[%t]-[%M]-[%L]-[%p] %m%n"/>
</layout>
</appender>
<root>
<level value="INFO" />
<appender-ref ref="CONSOLE" />
<appender-ref ref="FILE" />
</root>
</log4j:configuration>
\ No newline at end of file
......@@ -3,9 +3,9 @@
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.xxl.job.admin.service, com.xxl.job.admin.dao" />
......
......@@ -3,9 +3,9 @@
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
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/beans/spring-beans.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">
http://www.springframework.org/schema/tx/spring-tx.xsd">
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
......
......@@ -4,11 +4,11 @@
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/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<mvc:annotation-driven />
<context:component-scan base-package="com.xxl.job.admin.controller" />
......
......@@ -9,7 +9,6 @@ xxl.job.mail.host=smtp.163.com
xxl.job.mail.port=25
xxl.job.mail.username=ovono802302@163.com
xxl.job.mail.password=asdfzxcv
xxl.job.mail.sendFrom=ovono802302@163.com
xxl.job.mail.sendNick=《任务调度平台XXL-JOB》
### xxl-job login
......
......@@ -175,7 +175,7 @@
<#macro commonFooter >
<footer class="main-footer">
Powered by <b>XXL-JOB</b> 1.8.2(快照版本)
Powered by <b>XXL-JOB</b> 1.9.0(快照版本)
<div class="pull-right hidden-xs">
<strong>Copyright &copy; 2015-${.now?string('yyyy')} &nbsp;
<a href="http://www.xuxueli.com/" target="_blank" >xuxueli</a>
......
......@@ -4,6 +4,8 @@
<title>任务调度中心</title>
<#import "/common/common.macro.ftl" as netCommon>
<@netCommon.commonStyle />
<!-- daterangepicker -->
<link rel="stylesheet" href="${request.contextPath}/static/adminlte/plugins/daterangepicker/daterangepicker.css">
</head>
<body class="hold-transition skin-blue sidebar-mini <#if cookieMap?exists && "off" == cookieMap["xxljob_adminlte_settings"].value >sidebar-collapse</#if> ">
<div class="wrapper">
......@@ -43,7 +45,7 @@
<div class="progress">
<div class="progress-bar" style="width: 100%"></div>
</div>
<span class="progress-description">系统中配置的任务数量</span>
<span class="progress-description">调度中心运行的任务数量</span>
</div>
</div>
</div>
......@@ -82,7 +84,7 @@
<div class="progress">
<div class="progress-bar" style="width: 100%"></div>
</div>
<span class="progress-description">心跳检测成功的执行器机器数量</span>
<span class="progress-description">调度中心在线的执行器机器数量</span>
</div>
</div>
</div>
......@@ -94,8 +96,20 @@
<div class="col-md-12">
<div class="box">
<div class="box-header with-border">
<h3 class="box-title">调度报表(一月之内)</h3>
<h3 class="box-title">调度报表</h3>
<#--<input type="text" class="form-control" id="filterTime" readonly >-->
<!-- tools box -->
<div class="pull-right box-tools">
<button type="button" class="btn btn-primary btn-sm daterange pull-right" data-toggle="tooltip" id="filterTime" >
<i class="fa fa-calendar"></i>
</button>
<#--<button type="button" class="btn btn-primary btn-sm pull-right" data-widget="collapse" data-toggle="tooltip" title="" style="margin-right: 5px;" data-original-title="Collapse">
<i class="fa fa-minus"></i>
</button>-->
</div>
<!-- /. tools -->
</div>
<div class="box-body">
<div class="row">
......@@ -113,7 +127,6 @@
</div>
</div>
</section>
<!-- /.content -->
</div>
......@@ -123,10 +136,11 @@
<@netCommon.commonFooter />
</div>
<@netCommon.commonScript />
<#--<script src="${request.contextPath}/static/adminlte/plugins/daterangepicker/moment.min.js"></script>
<script src="${request.contextPath}/static/adminlte/plugins/daterangepicker/daterangepicker.js"></script>-->
<!-- daterangepicker -->
<script src="${request.contextPath}/static/adminlte/plugins/daterangepicker/moment.min.js"></script>
<script src="${request.contextPath}/static/adminlte/plugins/daterangepicker/daterangepicker.js"></script>
<#-- echarts -->
<script src="${request.contextPath}/static/plugins/echarts/echarts.common.min.js"></script>
<script src="${request.contextPath}/static/js/index.js"></script>
</body>
</html>
......@@ -109,15 +109,34 @@
</div>
<@netCommon.commonScript />
<#assign glueTypeModeSrc = "${request.contextPath}/static/plugins/codemirror/mode/clike/clike.js" />
<#assign glueTypeIdeMode = "text/x-java" />
<#if jobInfo.glueType == "GLUE_GROOVY" >
<#assign glueTypeModeSrc = "${request.contextPath}/static/plugins/codemirror/mode/clike/clike.js" />
<#assign glueTypeIdeMode = "text/x-java" />
<#elseif jobInfo.glueType == "GLUE_SHELL" >
<#assign glueTypeModeSrc = "${request.contextPath}/static/plugins/codemirror/mode/shell/shell.js" />
<#assign glueTypeIdeMode = "text/x-sh" />
<#elseif jobInfo.glueType == "GLUE_PYTHON" >
<#assign glueTypeModeSrc = "${request.contextPath}/static/plugins/codemirror/mode/python/python.js" />
<#assign glueTypeIdeMode = "text/x-python" />
<#elseif jobInfo.glueType == "GLUE_NODEJS" >
<#assign glueTypeModeSrc = "${request.contextPath}/static/plugins/codemirror/mode/javascript/javascript.js" />
<#assign glueTypeIdeMode = "text/javascript" />
</#if>
<script src="${request.contextPath}/static/plugins/codemirror/lib/codemirror.js"></script>
<script src="${request.contextPath}/static/plugins/codemirror/mode/clike/clike.js"></script>
<script src="${request.contextPath}/static/plugins/codemirror/mode/shell/shell.js"></script>
<script src="${request.contextPath}/static/plugins/codemirror/mode/python/python.js"></script>
<script src="${glueTypeModeSrc}"></script>
<script src="${request.contextPath}/static/plugins/codemirror/addon/hint/show-hint.js"></script>
<script src="${request.contextPath}/static/plugins/codemirror/addon/hint/anyword-hint.js"></script>
<script>
var id = '${jobInfo.id}';
var glueType = '${jobInfo.glueType}';
var ideMode = '${glueTypeIdeMode}';
</script>
<script src="${request.contextPath}/static/js/jobcode.index.1.js"></script>
......
......@@ -53,7 +53,20 @@
<td>${group.appName}</td>
<td>${group.title}</td>
<td><#if group.addressType==0>自动注册<#else>手动录入</#if></td>
<td><#if group.registryList?exists><#list group.registryList as item><span class="badge bg-green">${item}</span><br></#list></#if></td>
<td>
<#if group.registryList?exists>
<#list group.registryList as item>
<span class="badge bg-green" title="${item}" >
<#if item?length gt 35>
${item?substring(0, 35)}...
<#else>
${item}
</#if>
</span>
<br>
</#list>
</#if>
</td>
<td>
<button class="btn btn-warning btn-xs update"
id="${group.id}"
......@@ -107,7 +120,9 @@
</div>
<div class="form-group">
<label for="lastname" class="col-sm-2 control-label">机器地址<font color="red">*</font></label>
<div class="col-sm-10"><input type="text" class="form-control" name="addressList" placeholder="请输入执行器地址列表,多地址逗号分隔" maxlength="200" readonly="readonly" ></div>
<div class="col-sm-10">
<textarea class="textarea" name="addressList" maxlength="512" placeholder="请输入执行器地址列表,多地址逗号分隔" readonly="readonly" style="background-color:#eee; width: 100%; height: 100px; font-size: 14px; line-height: 10px; border: 1px solid #dddddd; padding: 10px;"></textarea>
</div>
</div>
<hr>
<div class="form-group">
......@@ -153,7 +168,9 @@
</div>
<div class="form-group">
<label for="lastname" class="col-sm-2 control-label">机器地址<font color="red">*</font></label>
<div class="col-sm-10"><input type="text" class="form-control" name="addressList" placeholder="请输入执行器地址列表,多地址逗号分隔" maxlength="200" readonly="readonly" ></div>
<div class="col-sm-10">
<textarea class="textarea" name="addressList" maxlength="512" placeholder="请输入执行器地址列表,多地址逗号分隔" readonly="readonly" style="background-color:#eee; width: 100%; height: 100px; font-size: 14px; line-height: 10px; border: 1px solid #dddddd; padding: 10px;"></textarea>
</div>
</div>
<hr>
<div class="form-group">
......
......@@ -63,7 +63,7 @@
<h3 class="box-title">调度列表</h3>
</div>
<div class="box-body" >
<table id="job_list" class="table table-bordered table-striped">
<table id="job_list" class="table table-bordered table-striped" width="100%" >
<thead>
<tr>
<th name="id" >id</th>
......@@ -127,7 +127,7 @@
</select>
</div>
<label for="lastname" class="col-sm-2 control-label">Cron<font color="red">*</font></label>
<div class="col-sm-4"><input type="text" class="form-control" name="jobCron" placeholder="请输入“Cron”" maxlength="20" ></div>
<div class="col-sm-4"><input type="text" class="form-control" name="jobCron" placeholder="请输入“Cron”" maxlength="128" ></div>
</div>
<div class="form-group">
<label for="firstname" class="col-sm-2 control-label">运行模式<font color="red">*</font></label>
......@@ -143,7 +143,7 @@
</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="executorParam" placeholder="请输入“执行参数”" maxlength="100" ></div>
<div class="col-sm-4"><input type="text" class="form-control" name="executorParam" placeholder="请输入“执行参数”" maxlength="512" ></div>
<label for="lastname" class="col-sm-2 control-label">子任务Key<font color="black">*</font></label>
<div class="col-sm-4"><input type="text" class="form-control" name="childJobKey" placeholder="请输入子任务的任务Key,如存在多个逗号分隔" maxlength="100" ></div>
</div>
......@@ -192,7 +192,7 @@ import com.xxl.job.core.handler.IJobHandler;
public class DemoGlueJobHandler extends IJobHandler {
@Override
public ReturnT<String> execute(String... params) throws Exception {
public ReturnT<String> execute(String param) throws Exception {
XxlJobLogger.log("XXL-JOB, Hello World.");
return ReturnT.SUCCESS;
}
......@@ -204,12 +204,15 @@ public class DemoGlueJobHandler extends IJobHandler {
echo "xxl-job: hello shell"
echo "脚本位置:$0"
echo "参数数量:$#"
echo "任务参数:$1"
echo "分片序号 = $2"
echo "分片总数 = $3"
<#--echo "参数数量:$#"
for param in $*
do
echo "参数 : $param"
sleep 1s
done
done-->
echo "Good bye!"
exit 0
......@@ -221,19 +224,40 @@ import time
import sys
print "xxl-job: hello python"
print "脚本文件:", sys.argv[0]
for i in range(1, len(sys.argv)):
print "任务参数:", sys.argv[1]
print "分片序号:", sys.argv[2]
print "分片总数:", sys.argv[3]
<#--for i in range(1, len(sys.argv)):
time.sleep(1)
print "参数", i, sys.argv[i]
print "参数", i, sys.argv[i]-->
print "Good bye!"
exit(0)<#--
exit(0)
<#--
import logging
logging.basicConfig(level=logging.DEBUG)
logging.info("脚本文件:" + sys.argv[0])
-->
</textarea>
<textarea class="glueSource_nodejs" style="display:none;" >
#!/usr/bin/env node
console.log("xxl-job: hello nodejs")
var arguments = process.argv
console.log("脚本文件: " + arguments[1])
console.log("任务参数: " + arguments[2])
console.log("分片序号: " + arguments[3])
console.log("分片总数: " + arguments[4])
<#--for (var i = 2; i < arguments.length; i++){
console.log("参数 %s = %s", (i-1), arguments[i]);
}-->
console.log("Good bye!")
process.exit(0)
</textarea>
</form>
</div>
</div>
......@@ -271,7 +295,7 @@ logging.info("脚本文件:" + sys.argv[0])
</select>
</div>
<label for="lastname" class="col-sm-2 control-label">Cron<font color="red">*</font></label>
<div class="col-sm-4"><input type="text" class="form-control" name="jobCron" placeholder="请输入“Cron”" maxlength="20" ></div>
<div class="col-sm-4"><input type="text" class="form-control" name="jobCron" placeholder="请输入“Cron”" maxlength="128" ></div>
</div>
<div class="form-group">
<label for="firstname" class="col-sm-2 control-label">运行模式<font color="red">*</font></label>
......@@ -287,7 +311,7 @@ logging.info("脚本文件:" + sys.argv[0])
</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="executorParam" placeholder="请输入“执行参数”" maxlength="100" ></div>
<div class="col-sm-4"><input type="text" class="form-control" name="executorParam" placeholder="请输入“执行参数”" maxlength="512" ></div>
<label for="lastname" class="col-sm-2 control-label">子任务Key<font color="black">*</font></label>
<div class="col-sm-4"><input type="text" class="form-control" name="childJobKey" placeholder="请输入子任务的任务Key,如存在多个逗号分隔" maxlength="100" ></div>
</div>
......
......@@ -7,14 +7,18 @@ $(function(){
$.post(base_url + "/logout", function(data, status) {
if (data.code == "200") {
layer.open({
layer.msg('注销成功');
setTimeout(function(){
window.location.href = base_url + "/";
}, 500);
/*layer.open({
title: '系统提示',
content: '注销成功',
icon: '1',
end: function(layero, index){
window.location.href = base_url + "/";
}
});
});*/
} else {
layer.open({
title: '系统提示',
......
......@@ -5,29 +5,74 @@
$(function () {
// 过滤时间
var _startDate = moment().subtract(1, 'months'); // 默认,最近一月
var _endDate = moment();
$('#filterTime').daterangepicker({
autoApply:false,
singleDatePicker:false,
showDropdowns:false, // 是否显示年月选择条件
timePicker: true, // 是否显示小时和分钟选择条件
timePickerIncrement: 10, // 时间的增量,单位为分钟
timePicker24Hour : true,
opens : 'left', //日期选择框的弹出位置
ranges: {
//'最近1小时': [moment().subtract(1, 'hours'), moment()],
'今日': [moment().startOf('day'), moment().endOf('day')],
'昨日': [moment().subtract(1, 'days').startOf('day'), moment().subtract(1, 'days').endOf('day')],
'本月': [moment().startOf('month'), moment().endOf('month')],
'上个月': [moment().subtract(1, 'months').startOf('month'), moment().subtract(1, 'months').endOf('month')],
'最近1周': [moment().subtract(1, 'weeks'), moment()],
'最近1月': [_startDate, _endDate]
},
locale : {
format: 'YYYY-MM-DD HH:mm:ss',
separator : ' - ',
customRangeLabel : '自定义',
applyLabel : '确定',
cancelLabel : '取消',
fromLabel : '起始时间',
toLabel : '结束时间',
daysOfWeek : [ '日', '一', '二', '三', '四', '五', '六' ],
monthNames : [ '一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月' ],
firstDay : 1
},
startDate:_startDate,
endDate: _endDate
}, function (start, end, label) {
freshChartDate(start, end);
});
freshChartDate(_startDate, _endDate);
/**
* 刷新报表
*
* @param startDate
* @param endDate
*/
$.ajax({
type : 'POST',
url : base_url + '/triggerChartDate',
data : { },
dataType : "json",
success : function(data){
if (data.code == 200) {
lineChartInit(data)
pieChartInit(data);
} else {
layer.open({
title: '系统提示',
content: (data.msg || '调度报表数据加载异常'),
icon: '2'
});
function freshChartDate(startDate, endDate) {
$.ajax({
type : 'POST',
url : base_url + '/triggerChartDate',
data : {
'startDate':startDate.format('YYYY-MM-DD HH:mm:ss'),
'endDate':endDate.format('YYYY-MM-DD HH:mm:ss')
},
dataType : "json",
success : function(data){
if (data.code == 200) {
lineChartInit(data)
pieChartInit(data);
} else {
layer.open({
title: '系统提示',
content: (data.msg || '调度报表数据加载异常'),
icon: '2'
});
}
}
}
});
});
}
/**
* 折线图
......@@ -151,38 +196,4 @@ $(function () {
pieChart.setOption(option);
}
// 过滤时间
/*$('#filterTime').daterangepicker({
autoApply:false,
singleDatePicker:false,
showDropdowns:false, // 是否显示年月选择条件
timePicker: true, // 是否显示小时和分钟选择条件
timePickerIncrement: 10, // 时间的增量,单位为分钟
timePicker24Hour : true,
opens : 'left', //日期选择框的弹出位置
ranges: {
'最近1小时': [moment().subtract(1, 'hours'), moment()],
'今日': [moment().startOf('day'), moment().endOf('day')],
'昨日': [moment().subtract(1, 'days').startOf('day'), moment().subtract(1, 'days').endOf('day')],
'最近7日': [moment().subtract(6, 'days'), moment()],
'最近30日': [moment().subtract(29, 'days'), moment()],
'本月': [moment().startOf('month'), moment().endOf('month')],
'上个月': [moment().subtract(1, 'month').startOf('month'), moment().subtract(1, 'month').endOf('month')]
},
locale : {
format: 'YYYY-MM-DD HH:mm:ss',
separator : ' - ',
customRangeLabel : '自定义',
applyLabel : '确定',
cancelLabel : '取消',
fromLabel : '起始时间',
toLabel : '结束时间',
daysOfWeek : [ '日', '一', '二', '三', '四', '五', '六' ],
monthNames : [ '一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月' ],
firstDay : 1,
startDate: moment().startOf('day'),
endDate: moment().endOf('day')
}
});*/
});
......@@ -8,34 +8,28 @@ $(function() {
});*/
var codeEditor;
function initIde(glueType, glueSource) {
var ideMode = "text/x-java";
if ('GLUE_GROOVY'==glueType){
ideMode = "text/x-java";
} else if ('GLUE_SHELL'==glueType){
ideMode = "text/x-sh";
} else if ('GLUE_PYTHON'==glueType){
ideMode = "text/x-python";
function initIde(glueSource) {
if (codeEditor == null) {
codeEditor = CodeMirror(document.getElementById("ideWindow"), {
mode : ideMode,
lineNumbers : true,
matchBrackets : true,
value: glueSource
});
} else {
codeEditor.setValue(glueSource);
}
codeEditor = CodeMirror(document.getElementById("ideWindow"), {
mode : ideMode,
lineNumbers : true,
matchBrackets : true,
value: glueSource
});
}
initIde(glueType, $("#version_now").val());
initIde($("#version_now").val());
// code change
$(".source_version").click(function(){
var glueType = $(this).attr('glueType');
var sourceId = $(this).attr('version');
var temp = $( "#" + sourceId ).val();
codeEditor.setValue('');
initIde(glueType, temp);
//codeEditor.setValue('');
initIde(temp);
});
// code source save
......
......@@ -122,11 +122,13 @@ $(function() {
// 注册方式,切换
$("#addModal input[name=addressType], #updateModal input[name=addressType]").click(function(){
var addressType = $(this).val();
var $addressList = $(this).parents("form").find("input[name=addressList]");
var $addressList = $(this).parents("form").find("textarea[name=addressList]");
if (addressType == 0) {
$addressList.css("background-color", "#eee"); // 自动注册
$addressList.attr("readonly","readonly");
$addressList.val("");
$addressList.attr("readonly","readonly");
} else {
$addressList.css("background-color", "white");
$addressList.removeAttr("readonly");
}
});
......@@ -144,7 +146,7 @@ $(function() {
//$("#updateModal .form input[name='addressType'][value='"+ addressType +"']").attr('checked', 'true');
$("#updateModal .form input[name='addressType'][value='"+ addressType +"']").click();
// 机器地址
$("#updateModal .form input[name='addressList']").val($(this).attr("addressList"));
$("#updateModal .form textarea[name='addressList']").val($(this).attr("addressList"));
$('#updateModal').modal({backdrop: false, keyboard: false}).modal('show');
});
......
......@@ -55,6 +55,8 @@ $(function() {
return "GLUE模式(Shell)";
} else if ('GLUE_PYTHON'==row.glueType) {
return "GLUE模式(Python)";
}else if ('GLUE_NODEJS'==row.glueType){
return "GLUE模式(Nodejs)";
} else if ('BEAN'==row.glueType) {
return "BEAN模式:" + row.executorHandler;
}
......@@ -341,6 +343,8 @@ $(function() {
$("#addModal .form textarea[name='glueSource']").val( $("#addModal .form .glueSource_shell").val() );
} else if ('GLUE_PYTHON'==glueType){
$("#addModal .form textarea[name='glueSource']").val( $("#addModal .form .glueSource_python").val() );
} else if ('GLUE_NODEJS'==glueType){
$("#addModal .form textarea[name='glueSource']").val( $("#addModal .form .glueSource_nodejs").val() );
}
});
......
......@@ -105,6 +105,8 @@ $(function() {
glueTypeTitle = "GLUE模式(Shell)";
} else if ('GLUE_PYTHON'==row.glueType) {
glueTypeTitle = "GLUE模式(Python)";
}else if ('GLUE_NODEJS'==row.glueType) {
glueTypeTitle = "GLUE模式(Nodejs)";
} else if ('BEAN'==row.glueType) {
glueTypeTitle = "BEAN模式:" + row.executorHandler;
}
......@@ -144,9 +146,16 @@ $(function() {
{
"data": 'triggerCode',
"render": function ( data, type, row ) {
return (data==200)?'<span style="color: green">成功</span>':(data==500)?'<span style="color: red">失败</span>':(data==0)?'':data;
var html = data;
if (data == 200) {
html = '<span style="color: green">成功</span>';
} else if (data == 500) {
html = '<span style="color: red">失败</span>';
} else if (data == 0) {
html = '';
}
return html;
}
},
{
"data": 'triggerMsg',
......@@ -163,7 +172,17 @@ $(function() {
{
"data": 'handleCode',
"render": function ( data, type, row ) {
return (data==200)?'<span style="color: green">成功</span>':(data==500)?'<span style="color: red">失败</span>':(data==0)?'':data;
var html = data;
if (data == 200) {
html = '<span style="color: green">成功</span>';
} else if (data == 500) {
html = '<span style="color: red">失败</span>';
} else if (data == 501) {
html = '<span style="color: red">失败重试</span>';
} else if (data == 0) {
html = '';
}
return html;
}
},
{
......
......@@ -48,14 +48,18 @@ $(function(){
submitHandler : function(form) {
$.post(base_url + "/login", $("#loginForm").serialize(), function(data, status) {
if (data.code == "200") {
layer.open({
layer.msg('登录成功');
setTimeout(function(){
window.location.href = base_url;
}, 500);
/*layer.open({
title: '系统提示',
content: '登录成功',
icon: '1',
end: function(layero, index){
window.location.href = base_url;
}
});
});*/
} else {
layer.open({
title: '系统提示',
......
package com.xxl.job.admin.controller;
import org.junit.Before;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
@WebAppConfiguration
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath*:spring/*.xml"})
public class AbstractSpringMvcTest {
@Autowired
private WebApplicationContext applicationContext;
protected MockMvc mockMvc;
@Before
public void setup() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.applicationContext).build();
}
}
package com.xxl.job.admin.controller;
import com.xxl.job.admin.controller.interceptor.PermissionInterceptor;
import com.xxl.job.admin.core.util.PropertiesUtil;
import org.junit.Before;
import org.junit.Test;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import javax.servlet.http.Cookie;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
public class JobInfoControllerTest extends AbstractSpringMvcTest {
private Cookie cookie;
@Before
public void login() throws Exception {
MvcResult ret = mockMvc.perform(
post("/login")
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.param("userName", PropertiesUtil.getString("xxl.job.login.username"))
.param("password", PropertiesUtil.getString("xxl.job.login.password"))
).andReturn();
cookie = ret.getResponse().getCookie(PermissionInterceptor.LOGIN_IDENTITY_KEY);
}
@Test
public void testAdd() throws Exception {
MultiValueMap<String, String> parameters = new LinkedMultiValueMap<String, String>();
parameters.add("jobGroup", "1");
MvcResult ret = mockMvc.perform(
post("/jobinfo/pageList")
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
//.content(paramsJson)
.params(parameters)
.cookie(cookie)
).andReturn();
System.out.println(ret.getResponse().getContentAsString());
}
}
package com.xxl.job.dao.impl;
package com.xxl.job.admin.dao;
import com.xxl.job.admin.core.model.XxlJobGroup;
import com.xxl.job.admin.dao.XxlJobGroupDao;
......
package com.xxl.job.dao.impl;
import com.xxl.job.admin.core.model.XxlJobInfo;
import com.xxl.job.admin.dao.XxlJobInfoDao;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import javax.annotation.Resource;
import java.util.Date;
import java.util.List;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath*:spring/applicationcontext-*.xml")
public class XxlJobInfoTest {
@Resource
private XxlJobInfoDao xxlJobInfoDao;
@Test
public void pageList(){
List<XxlJobInfo> list = xxlJobInfoDao.pageList(0, 20, 0, null);
int list_count = xxlJobInfoDao.pageListCount(0, 20, 0, null);
System.out.println(list);
System.out.println(list_count);
List<XxlJobInfo> list2 = xxlJobInfoDao.getJobsByGroup(1);
}
@Test
public void save_load(){
XxlJobInfo info = new XxlJobInfo();
info.setJobGroup(1);
info.setJobCron("jobCron");
info.setJobDesc("desc");
info.setAuthor("setAuthor");
info.setAlarmEmail("setAlarmEmail");
info.setExecutorRouteStrategy("setExecutorRouteStrategy");
info.setExecutorHandler("setExecutorHandler");
info.setExecutorParam("setExecutorParam");
info.setExecutorBlockStrategy("setExecutorBlockStrategy");
info.setExecutorFailStrategy("setExecutorFailStrategy");
info.setGlueType("setGlueType");
info.setGlueSource("setGlueSource");
info.setGlueRemark("setGlueRemark");
info.setChildJobKey("setChildJobKey");
int count = xxlJobInfoDao.save(info);
XxlJobInfo info2 = xxlJobInfoDao.loadById(info.getId());
info2.setJobCron("jobCron2");
info2.setJobDesc("desc2");
info2.setAuthor("setAuthor2");
info2.setAlarmEmail("setAlarmEmail2");
info2.setExecutorRouteStrategy("setExecutorRouteStrategy2");
info2.setExecutorHandler("setExecutorHandler2");
info2.setExecutorParam("setExecutorParam2");
info2.setExecutorBlockStrategy("setExecutorBlockStrategy2");
info2.setExecutorFailStrategy("setExecutorFailStrategy2");
info2.setGlueType("setGlueType2");
info2.setGlueSource("setGlueSource2");
info2.setGlueRemark("setGlueRemark2");
info2.setGlueUpdatetime(new Date());
info2.setChildJobKey("setChildJobKey2");
int item2 = xxlJobInfoDao.update(info2);
xxlJobInfoDao.delete(info2.getId());
List<XxlJobInfo> list2 = xxlJobInfoDao.getJobsByGroup(1);
int ret3 = xxlJobInfoDao.findAllCount();
}
}
package com.xxl.job.admin.dao;
import com.xxl.job.admin.core.model.XxlJobInfo;
import com.xxl.job.admin.dao.XxlJobInfoDao;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import javax.annotation.Resource;
import java.util.Date;
import java.util.List;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath*:spring/applicationcontext-*.xml")
public class XxlJobInfoDaoTest {
@Resource
private XxlJobInfoDao xxlJobInfoDao;
@Test
public void pageList(){
List<XxlJobInfo> list = xxlJobInfoDao.pageList(0, 20, 0, null);
int list_count = xxlJobInfoDao.pageListCount(0, 20, 0, null);
System.out.println(list);
System.out.println(list_count);
List<XxlJobInfo> list2 = xxlJobInfoDao.getJobsByGroup(1);
}
@Test
public void save_load(){
XxlJobInfo info = new XxlJobInfo();
info.setJobGroup(1);
info.setJobCron("jobCron");
info.setJobDesc("desc");
info.setAuthor("setAuthor");
info.setAlarmEmail("setAlarmEmail");
info.setExecutorRouteStrategy("setExecutorRouteStrategy");
info.setExecutorHandler("setExecutorHandler");
info.setExecutorParam("setExecutorParam");
info.setExecutorBlockStrategy("setExecutorBlockStrategy");
info.setExecutorFailStrategy("setExecutorFailStrategy");
info.setGlueType("setGlueType");
info.setGlueSource("setGlueSource");
info.setGlueRemark("setGlueRemark");
info.setChildJobKey("setChildJobKey");
int count = xxlJobInfoDao.save(info);
XxlJobInfo info2 = xxlJobInfoDao.loadById(info.getId());
info2.setJobCron("jobCron2");
info2.setJobDesc("desc2");
info2.setAuthor("setAuthor2");
info2.setAlarmEmail("setAlarmEmail2");
info2.setExecutorRouteStrategy("setExecutorRouteStrategy2");
info2.setExecutorHandler("setExecutorHandler2");
info2.setExecutorParam("setExecutorParam2");
info2.setExecutorBlockStrategy("setExecutorBlockStrategy2");
info2.setExecutorFailStrategy("setExecutorFailStrategy2");
info2.setGlueType("setGlueType2");
info2.setGlueSource("setGlueSource2");
info2.setGlueRemark("setGlueRemark2");
info2.setGlueUpdatetime(new Date());
info2.setChildJobKey("setChildJobKey2");
int item2 = xxlJobInfoDao.update(info2);
xxlJobInfoDao.delete(info2.getId());
List<XxlJobInfo> list2 = xxlJobInfoDao.getJobsByGroup(1);
int ret3 = xxlJobInfoDao.findAllCount();
}
}
package com.xxl.job.dao.impl;
package com.xxl.job.admin.dao;
import com.xxl.job.admin.core.model.XxlJobLog;
import com.xxl.job.admin.dao.XxlJobLogDao;
import org.apache.commons.lang.time.DateUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
......
package com.xxl.job.dao.impl;
package com.xxl.job.admin.dao;
import com.xxl.job.admin.core.model.XxlJobLogGlue;
import com.xxl.job.admin.dao.XxlJobLogGlueDao;
......
package com.xxl.job.dao.impl;
package com.xxl.job.admin.dao;
import com.xxl.job.admin.core.model.XxlJobRegistry;
import com.xxl.job.admin.dao.XxlJobRegistryDao;
......
package com.xxl.job.admin.util;
import com.xxl.job.admin.core.util.MailUtil;
import org.junit.Test;
import java.text.MessageFormat;
/**
* email util test
*
* @author xuxueli 2017-12-22 17:16:23
*/
public class MailUtilTest {
@Test
public void registryTest() throws Exception {
String mailBodyTemplate = "<h5>监控告警明细:</span>" +
"<table border=\"1\" cellpadding=\"3\" style=\"border-collapse:collapse; width:80%;\" >\n" +
" <thead style=\"font-weight: bold;color: #ffffff;background-color: #ff8c00;\" >" +
" <tr>\n" +
" <td>执行器</td>\n" +
" <td>JobKey</td>\n" +
" <td>任务描述</td>\n" +
" <td>告警类型</td>\n" +
" </tr>\n" +
" <thead/>\n" +
" <tbody>\n" +
" <tr>\n" +
" <td>{0}</td>\n" +
" <td>{1}</td>\n" +
" <td>{2}</td>\n" +
" <td>调度失败</td>\n" +
" </tr>\n" +
" <tbody>\n" +
"</table>";
mailBodyTemplate = MessageFormat.format(mailBodyTemplate, "执行器A", "1_1", "任务A1");
boolean ret = MailUtil.sendMail("931591021@qq.com", "调度中心监控报警" , mailBodyTemplate);
System.out.println(ret);
}
}
package com.xxl.job.admin.util;
import com.xxl.job.admin.core.util.PropertiesUtil;
import org.junit.Test;
/**
* prop util test
*
* @author xuxueli 2017-12-25 15:17:36
*/
public class PropertiesUtilTest {
@Test
public void registryTest() throws Exception {
System.out.println(PropertiesUtil.getString("xxl.job.login.username"));
}
}
package com.xxl.job.dao.impl;
package com.xxl.job.adminbiz;
import com.xxl.job.core.biz.AdminBiz;
import com.xxl.job.core.biz.model.RegistryParam;
......@@ -9,7 +9,8 @@ import org.junit.Assert;
import org.junit.Test;
/**
* admin-api client, test
* admin api test
*
* @author xuxueli 2017-07-28 22:14:52
*/
public class AdminBizTest {
......@@ -18,6 +19,11 @@ public class AdminBizTest {
private static String addressUrl = "http://127.0.0.1:8080/xxl-job-admin".concat(AdminBiz.MAPPING);
private static String accessToken = null;
/**
* registry executor
*
* @throws Exception
*/
@Test
public void registryTest() throws Exception {
AdminBiz adminBiz = (AdminBiz) new NetComClientProxy(AdminBiz.class, addressUrl, accessToken).getObject();
......@@ -28,6 +34,11 @@ public class AdminBizTest {
Assert.assertTrue(returnT.getCode() == ReturnT.SUCCESS_CODE);
}
/**
* registry executor remove
*
* @throws Exception
*/
@Test
public void registryRemove() throws Exception {
AdminBiz adminBiz = (AdminBiz) new NetComClientProxy(AdminBiz.class, addressUrl, accessToken).getObject();
......@@ -38,6 +49,11 @@ public class AdminBizTest {
Assert.assertTrue(returnT.getCode() == ReturnT.SUCCESS_CODE);
}
/**
* trigger job for once
*
* @throws Exception
*/
@Test
public void triggerJob() throws Exception {
AdminBiz adminBiz = (AdminBiz) new NetComClientProxy(AdminBiz.class, addressUrl, accessToken).getObject();
......
......@@ -4,7 +4,7 @@
<parent>
<groupId>com.xuxueli</groupId>
<artifactId>xxl-job</artifactId>
<version>1.8.2-SNAPSHOT</version>
<version>1.9.0-SNAPSHOT</version>
</parent>
<artifactId>xxl-job-core</artifactId>
<packaging>jar</packaging>
......@@ -49,11 +49,11 @@
<!-- jackson -->
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>${jackson-mapper-asl.version}</version>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
</dependency>
<!-- httpclient -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
......
......@@ -59,7 +59,7 @@ public class ExecutorBizImpl implements ExecutorBiz {
@Override
public ReturnT<LogResult> log(long logDateTim, int logId, int fromLineNum) {
// log filename: yyyy-MM-dd/9999.log
// log filename: logPath/yyyy-MM-dd/9999.log
String logFileName = XxlJobFileAppender.makeLogFileName(new Date(logDateTim), logId);
LogResult logResult = XxlJobFileAppender.readLog(logFileName, fromLineNum);
......@@ -74,7 +74,8 @@ public class ExecutorBizImpl implements ExecutorBiz {
String removeOldReason = null;
// valid:jobHandler + jobThread
if (GlueTypeEnum.BEAN==GlueTypeEnum.match(triggerParam.getGlueType())) {
GlueTypeEnum glueTypeEnum = GlueTypeEnum.match(triggerParam.getGlueType());
if (GlueTypeEnum.BEAN == glueTypeEnum) {
// new jobhandler
IJobHandler newJobHandler = XxlJobExecutor.loadJobHandler(triggerParam.getExecutorHandler());
......@@ -96,7 +97,7 @@ public class ExecutorBizImpl implements ExecutorBiz {
}
}
} else if (GlueTypeEnum.GLUE_GROOVY==GlueTypeEnum.match(triggerParam.getGlueType())) {
} else if (GlueTypeEnum.GLUE_GROOVY == glueTypeEnum) {
// valid old jobThread
if (jobThread != null &&
......@@ -119,8 +120,7 @@ public class ExecutorBizImpl implements ExecutorBiz {
return new ReturnT<String>(ReturnT.FAIL_CODE, e.getMessage());
}
}
} else if (GlueTypeEnum.GLUE_SHELL==GlueTypeEnum.match(triggerParam.getGlueType())
|| GlueTypeEnum.GLUE_PYTHON==GlueTypeEnum.match(triggerParam.getGlueType()) ) {
} else if (glueTypeEnum!=null && glueTypeEnum.isScript()) {
// valid old jobThread
if (jobThread != null &&
......
......@@ -4,11 +4,12 @@ import com.xxl.job.core.biz.AdminBiz;
import com.xxl.job.core.biz.ExecutorBiz;
import com.xxl.job.core.biz.impl.ExecutorBizImpl;
import com.xxl.job.core.handler.IJobHandler;
import com.xxl.job.core.handler.annotation.JobHander;
import com.xxl.job.core.handler.annotation.JobHandler;
import com.xxl.job.core.log.XxlJobFileAppender;
import com.xxl.job.core.rpc.netcom.NetComClientProxy;
import com.xxl.job.core.rpc.netcom.NetComServerFactory;
import com.xxl.job.core.thread.JobThread;
import com.xxl.job.core.util.NetUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
......@@ -28,7 +29,7 @@ public class XxlJobExecutor implements ApplicationContextAware {
// ---------------------- param ----------------------
private String ip;
private int port = 9999;
private int port;
private String appName;
private String adminAddresses;
private String accessToken;
......@@ -71,14 +72,10 @@ public class XxlJobExecutor implements ApplicationContextAware {
initAdminBizList(adminAddresses, accessToken);
// init executor-jobHandlerRepository
if (applicationContext != null) {
initJobHandlerRepository(applicationContext);
}
initJobHandlerRepository(applicationContext);
// init logpath
if (logPath!=null && logPath.trim().length()>0) {
XxlJobFileAppender.logPath = logPath;
}
XxlJobFileAppender.initLogPath(logPath);
// init executor-server
initExecutorServer(port, ip, appName, accessToken);
......@@ -121,6 +118,10 @@ public class XxlJobExecutor implements ApplicationContextAware {
// ---------------------- executor-server(jetty) ----------------------
private NetComServerFactory serverFactory = new NetComServerFactory();
private void initExecutorServer(int port, String ip, String appName, String accessToken) throws Exception {
// valid param
port = port>0?port: NetUtil.findAvailablePort(9999);
// start server
NetComServerFactory.putService(ExecutorBiz.class, new ExecutorBizImpl()); // rpc-service, base on jetty
NetComServerFactory.setAccessToken(accessToken);
serverFactory.start(port, ip, appName); // jetty + registry
......@@ -140,13 +141,17 @@ public class XxlJobExecutor implements ApplicationContextAware {
return jobHandlerRepository.get(name);
}
private static void initJobHandlerRepository(ApplicationContext applicationContext){
if (applicationContext == null) {
return;
}
// init job handler action
Map<String, Object> serviceBeanMap = applicationContext.getBeansWithAnnotation(JobHander.class);
Map<String, Object> serviceBeanMap = applicationContext.getBeansWithAnnotation(JobHandler.class);
if (serviceBeanMap!=null && serviceBeanMap.size()>0) {
for (Object serviceBean : serviceBeanMap.values()) {
if (serviceBean instanceof IJobHandler){
String name = serviceBean.getClass().getAnnotation(JobHander.class).value();
String name = serviceBean.getClass().getAnnotation(JobHandler.class).value();
IJobHandler handler = (IJobHandler) serviceBean;
if (loadJobHandler(name) != null) {
throw new RuntimeException("xxl-job jobhandler naming conflicts.");
......
......@@ -5,19 +5,40 @@ package com.xxl.job.core.glue;
*/
public enum GlueTypeEnum {
BEAN("BEAN模式"),
GLUE_GROOVY("GLUE模式(Java)"),
GLUE_SHELL("GLUE模式(Shell)"),
GLUE_PYTHON("GLUE模式(Python)");
BEAN("BEAN模式", false, null, null),
GLUE_GROOVY("GLUE模式(Java)", false, null, null),
GLUE_SHELL("GLUE模式(Shell)", true, "bash", ".sh"),
GLUE_PYTHON("GLUE模式(Python)", true, "python", ".py"),
GLUE_NODEJS("GLUE模式(Nodejs)", true, "node", ".js");
private String desc;
private GlueTypeEnum(String desc) {
private boolean isScript;
private String cmd;
private String suffix;
private GlueTypeEnum(String desc, boolean isScript, String cmd, String suffix) {
this.desc = desc;
this.isScript = isScript;
this.cmd = cmd;
this.suffix = suffix;
}
public String getDesc() {
return desc;
}
public boolean isScript() {
return isScript;
}
public String getCmd() {
return cmd;
}
public String getSuffix() {
return suffix;
}
public static GlueTypeEnum match(String name){
for (GlueTypeEnum item: GlueTypeEnum.values()) {
if (item.name().equals(name)) {
......@@ -26,4 +47,5 @@ public enum GlueTypeEnum {
}
return null;
}
}
......@@ -3,17 +3,45 @@ package com.xxl.job.core.handler;
import com.xxl.job.core.biz.model.ReturnT;
/**
* remote job handler
* job handler
*
* @author xuxueli 2015-12-19 19:06:38
*/
public abstract class IJobHandler {
/** success */
public static final ReturnT<String> SUCCESS = new ReturnT<String>(200, null);
/** fail */
public static final ReturnT<String> FAIL = new ReturnT<String>(500, null);
/** fail retry */
public static final ReturnT<String> FAIL_RETRY = new ReturnT<String>(501, null);
/**
* job handler
* @param params
* execute handler, invoked when executor receives a scheduling request
*
* @param param
* @return
* @throws Exception
*/
public abstract ReturnT<String> execute(String... params) throws Exception;
public abstract ReturnT<String> execute(String param) throws Exception;
/**
* init handler, invoked when JobThread init
*/
public void init() {
// TODO
}
/**
* destroy handler, invoked when JobThread destroy
*/
public void destroy() {
// TODO
}
}
......@@ -13,7 +13,7 @@ import java.lang.annotation.Target;
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface JobHander {
public @interface JobHandler {
String value() default "";
......
......@@ -21,9 +21,9 @@ public class GlueJobHandler extends IJobHandler {
}
@Override
public ReturnT<String> execute(String... params) throws Exception {
public ReturnT<String> execute(String param) throws Exception {
XxlJobLogger.log("----------- glue.version:"+ glueUpdatetime +" -----------");
return jobHandler.execute(params);
return jobHandler.execute(param);
}
}
......@@ -6,6 +6,7 @@ import com.xxl.job.core.handler.IJobHandler;
import com.xxl.job.core.log.XxlJobFileAppender;
import com.xxl.job.core.log.XxlJobLogger;
import com.xxl.job.core.util.ScriptUtil;
import com.xxl.job.core.util.ShardingUtil;
/**
* Created by xuxueli on 17/4/27.
......@@ -29,29 +30,38 @@ public class ScriptJobHandler extends IJobHandler {
}
@Override
public ReturnT<String> execute(String... params) throws Exception {
// cmd + script-file-name
String cmd = "bash";
String scriptFileName = null;
if (GlueTypeEnum.GLUE_SHELL == glueType) {
cmd = "bash";
scriptFileName = XxlJobFileAppender.logPath.concat("gluesource/").concat(String.valueOf(jobId)).concat("_").concat(String.valueOf(glueUpdatetime)).concat(".sh");
} else if (GlueTypeEnum.GLUE_PYTHON == glueType) {
cmd = "python";
scriptFileName = XxlJobFileAppender.logPath.concat("gluesource/").concat(String.valueOf(jobId)).concat("_").concat(String.valueOf(glueUpdatetime)).concat(".py");
public ReturnT<String> execute(String param) throws Exception {
if (!glueType.isScript()) {
return new ReturnT<String>(IJobHandler.FAIL.getCode(), "glueType["+ glueType +"] invalid.");
}
// cmd
String cmd = glueType.getCmd();
// make script file
String scriptFileName = XxlJobFileAppender.getLogPath()
.concat("/gluesource/")
.concat(String.valueOf(jobId))
.concat("_")
.concat(String.valueOf(glueUpdatetime))
.concat(glueType.getSuffix());
ScriptUtil.markScriptFile(scriptFileName, gluesource);
// log file
String logFileName = XxlJobFileAppender.logPath.concat(XxlJobFileAppender.contextHolder.get());
String logFileName = XxlJobFileAppender.contextHolder.get();
// script params:0=param、1=分片序号、2=分片总数
ShardingUtil.ShardingVO shardingVO = ShardingUtil.getShardingVo();
String[] scriptParams = new String[3];
scriptParams[0] = param;
scriptParams[1] = String.valueOf(shardingVO.getIndex());
scriptParams[2] = String.valueOf(shardingVO.getTotal());
// invoke
XxlJobLogger.log("----------- script file:"+ scriptFileName +" -----------");
int exitValue = ScriptUtil.execToFile(cmd, scriptFileName, logFileName, params);
ReturnT<String> result = (exitValue==0)?ReturnT.SUCCESS:new ReturnT<String>(ReturnT.FAIL_CODE, "script exit value("+exitValue+") is failed");
int exitValue = ScriptUtil.execToFile(cmd, scriptFileName, logFileName, scriptParams);
ReturnT<String> result = (exitValue==0)?IJobHandler.SUCCESS:new ReturnT<String>(IJobHandler.FAIL.getCode(), "script exit value("+exitValue+") is failed");
return result;
}
......
......@@ -18,11 +18,35 @@ public class XxlJobFileAppender {
// for JobThread (support log for child thread of job handler)
//public static ThreadLocal<String> contextHolder = new ThreadLocal<String>();
public static final InheritableThreadLocal<String> contextHolder = new InheritableThreadLocal<String>();
public static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); // TODO, concurrent issues
public static String logPath = "/data/applogs/xxl-job/jobhandler/";
// log base path
private static String logBasePath = "/data/applogs/xxl-job/jobhandler";
public static void initLogPath(String logPath){
// init
if (logPath!=null && logPath.trim().length()>0) {
logBasePath = logPath;
}
// mk base dir
File logPathDir = new File(logBasePath);
if (!logPathDir.exists()) {
logPathDir.mkdirs();
}
logBasePath = logPathDir.getPath();
// mk glue dir
File glueBaseDir = new File(logPathDir, "gluesource");
if (!glueBaseDir.exists()) {
glueBaseDir.mkdirs();
}
}
public static String getLogPath() {
return logBasePath;
}
/**
* log filename: yyyy-MM-dd/9999.log
* log filename, like "logPath/yyyy-MM-dd/9999.log"
*
* @param triggerDate
* @param logId
......@@ -30,21 +54,18 @@ public class XxlJobFileAppender {
*/
public static String makeLogFileName(Date triggerDate, int logId) {
// filePath/
File filePathDir = new File(logPath);
if (!filePathDir.exists()) {
filePathDir.mkdirs();
}
// filePath/yyyy-MM-dd/
String nowFormat = sdf.format(new Date());
File filePathDateDir = new File(filePathDir, nowFormat);
if (!filePathDateDir.exists()) {
filePathDateDir.mkdirs();
}
// filePath/yyyy-MM-dd/9999.log
String logFileName = XxlJobFileAppender.sdf.format(triggerDate).concat("/").concat(String.valueOf(logId)).concat(".log");
// filePath/yyyy-MM-dd
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); // avoid concurrent problem, can not be static
File logFilePath = new File(getLogPath(), sdf.format(triggerDate));
if (!logFilePath.exists()) {
logFilePath.mkdir();
}
// filePath/yyyy-MM-dd/9999.log
String logFileName = logFilePath.getPath()
.concat("/")
.concat(String.valueOf(logId))
.concat(".log");
return logFileName;
}
......@@ -56,17 +77,11 @@ public class XxlJobFileAppender {
*/
public static void appendLog(String logFileName, String appendLog) {
// log
if (appendLog == null) {
appendLog = "";
}
appendLog += "\r\n";
// log file
if (logFileName==null || logFileName.trim().length()==0) {
return;
}
File logFile = new File(logPath, logFileName);
File logFile = new File(logFileName);
if (!logFile.exists()) {
try {
......@@ -76,6 +91,12 @@ public class XxlJobFileAppender {
return;
}
}
// log
if (appendLog == null) {
appendLog = "";
}
appendLog += "\r\n";
// append file content
try {
......@@ -111,7 +132,7 @@ public class XxlJobFileAppender {
if (logFileName==null || logFileName.trim().length()==0) {
return new LogResult(fromLineNum, 0, "readLog fail, logFile not found", true);
}
File logFile = new File(logPath, logFileName);
File logFile = new File(logFileName);
if (!logFile.exists()) {
return new LogResult(fromLineNum, 0, "readLog fail, logFile not exists", true);
......
......@@ -3,6 +3,8 @@ package com.xxl.job.core.log;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.text.MessageFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
......@@ -12,51 +14,68 @@ import java.util.Date;
*/
public class XxlJobLogger {
private static Logger logger = LoggerFactory.getLogger("xxl-job logger");
private static SimpleDateFormat xxlJobLoggerFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
private static SimpleDateFormat xxlJobLoggerFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); // TODO,concurrent issue
/**
* append log
*
* @param callInfo
* @param appendLog
*/
public static void log(String appendLog) {
private static void logDetail(StackTraceElement callInfo, String appendLog) {
// logFileName
String logFileName = XxlJobFileAppender.contextHolder.get();
if (logFileName==null || logFileName.trim().length()==0) {
return;
}
// "yyyy-MM-dd HH:mm:ss [ClassName]-[MethodName]-[LineNumber]-[ThreadName] log";
/*// "yyyy-MM-dd HH:mm:ss [ClassName]-[MethodName]-[LineNumber]-[ThreadName] log";
StackTraceElement[] stackTraceElements = new Throwable().getStackTrace();
StackTraceElement callInfo = stackTraceElements[1];
StackTraceElement callInfo = stackTraceElements[1];*/
StringBuffer stringBuffer = new StringBuffer();
stringBuffer.append(xxlJobLoggerFormat.format(new Date())).append(" ")
.append("["+ callInfo.getClassName() +"]").append("-")
.append("["+ callInfo.getMethodName() +"]").append("-")
.append("["+ callInfo.getClassName() + "#" + callInfo.getMethodName() +"]").append("-")
.append("["+ callInfo.getLineNumber() +"]").append("-")
.append("["+ Thread.currentThread().getName() +"]").append(" ")
.append(appendLog!=null?appendLog:"");
String formatAppendLog = stringBuffer.toString();
// appendlog
XxlJobFileAppender.appendLog(logFileName, formatAppendLog);
logger.warn("[{}]: {}", logFileName, formatAppendLog);
String logFileName = XxlJobFileAppender.contextHolder.get();
if (logFileName!=null && logFileName.trim().length()>0) {
XxlJobFileAppender.appendLog(logFileName, formatAppendLog);
} else {
logger.info(">>>>>>>>>>> {}", formatAppendLog);
}
}
/**
* append log with pattern
*
* @
*
* @param appendLogPattern like "aaa {0} bbb {1} ccc"
* @param appendLogArguments like "111, true"
*/
public static void log(String appendLogPattern, Object ... appendLogArguments) {
String appendLog = MessageFormat.format(appendLogPattern, appendLogArguments);
log(appendLog);
String appendLog = appendLogPattern;
if (appendLogArguments!=null && appendLogArguments.length>0) {
appendLog = MessageFormat.format(appendLogPattern, appendLogArguments);
}
StackTraceElement callInfo = new Throwable().getStackTrace()[1];
logDetail(callInfo, appendLog);
}
/**
* append exception stack
*
* @param e
*/
public static void log(Throwable e) {
StringWriter stringWriter = new StringWriter();
e.printStackTrace(new PrintWriter(stringWriter));
String appendLog = stringWriter.toString();
StackTraceElement callInfo = new Throwable().getStackTrace()[1];
logDetail(callInfo, appendLog);
}
}
......@@ -36,6 +36,12 @@ public class NetComClientProxy implements FactoryBean<Object> {
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// filter method like "Object.toString()"
if (Object.class.getName().equals(method.getDeclaringClass().getName())) {
logger.error(">>>>>>>>>>> xxl-rpc proxy class-method not support [{}.{}]", method.getDeclaringClass().getName(), method.getName());
throw new RuntimeException("xxl-rpc proxy class-method not support");
}
// request
RpcRequest request = new RpcRequest();
......@@ -46,7 +52,7 @@ public class NetComClientProxy implements FactoryBean<Object> {
request.setMethodName(method.getName());
request.setParameterTypes(method.getParameterTypes());
request.setParameters(args);
// send
RpcResponse response = client.send(request);
......
......@@ -44,7 +44,7 @@ public class JettyServer {
try {
// Start server
server.start();
logger.info(">>>>>>>>>>>> xxl-job jetty server start success at port:{}.", port);
logger.info(">>>>>>>>>>> xxl-job jetty server start success at port:{}.", port);
// Start Registry-Server
ExecutorRegistryThread.getInstance().start(port, ip, appName);
......
......@@ -28,11 +28,11 @@ public class ExecutorRegistryThread extends Thread {
// valid
if (appName==null || appName.trim().length()==0) {
logger.warn(">>>>>>>>>>>> xxl-job, executor registry config fail, appName is null.");
logger.warn(">>>>>>>>>>> xxl-job, executor registry config fail, appName is null.");
return;
}
if (XxlJobExecutor.getAdminBizList() == null) {
logger.warn(">>>>>>>>>>>> xxl-job, executor registry config fail, adminAddresses is null.");
logger.warn(">>>>>>>>>>> xxl-job, executor registry config fail, adminAddresses is null.");
return;
}
......@@ -99,7 +99,7 @@ public class ExecutorRegistryThread extends Thread {
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
logger.warn(">>>>>>>>>>>> xxl-job, executor registry thread destory.");
logger.info(">>>>>>>>>>> xxl-job, executor registry thread destory.");
}
});
......
......@@ -57,7 +57,7 @@ public class JobThread extends Thread{
public ReturnT<String> pushTriggerQueue(TriggerParam triggerParam) {
// avoid repeat
if (triggerLogIdSet.contains(triggerParam.getLogId())) {
logger.debug("repeate trigger job, logId:{}", triggerParam.getLogId());
logger.info(">>>>>>>>>>> repeate trigger job, logId:{}", triggerParam.getLogId());
return new ReturnT<String>(ReturnT.FAIL_CODE, "repeate trigger job, logId:" + triggerParam.getLogId());
}
......@@ -91,59 +91,42 @@ public class JobThread extends Thread{
@Override
public void run() {
// init
try {
handler.init();
} catch (Throwable e) {
logger.error(e.getMessage(), e);
}
// execute
while(!toStop){
running = false;
idleTimes++;
try {
TriggerParam triggerParam = null;
ReturnT<String> executeResult = null;
try {
// to check toStop signal, we need cycle, so wo cannot use queue.take(), instand of poll(timeout)
TriggerParam triggerParam = triggerQueue.poll(3L, TimeUnit.SECONDS);
triggerParam = triggerQueue.poll(3L, TimeUnit.SECONDS);
if (triggerParam!=null) {
running = true;
idleTimes = 0;
triggerLogIdSet.remove(triggerParam.getLogId());
// parse param
String[] handlerParams = (triggerParam.getExecutorParams()!=null && triggerParam.getExecutorParams().trim().length()>0)
? (String[])(Arrays.asList(triggerParam.getExecutorParams().split(",")).toArray()) : null;
// handle job
ReturnT<String> executeResult = null;
try {
// log filename: yyyy-MM-dd/9999.log
String logFileName = XxlJobFileAppender.makeLogFileName(new Date(triggerParam.getLogDateTim()), triggerParam.getLogId());
XxlJobFileAppender.contextHolder.set(logFileName);
ShardingUtil.setShardingVo(new ShardingUtil.ShardingVO(triggerParam.getBroadcastIndex(), triggerParam.getBroadcastTotal()));
XxlJobLogger.log("<br>----------- xxl-job job execute start -----------<br>----------- Params:" + Arrays.toString(handlerParams));
executeResult = handler.execute(handlerParams);
if (executeResult == null) {
executeResult = ReturnT.FAIL;
}
XxlJobLogger.log("<br>----------- xxl-job job execute end(finish) -----------<br>----------- ReturnT:" + executeResult);
} catch (Exception e) {
if (toStop) {
XxlJobLogger.log("<br>----------- JobThread toStop, stopReason:" + stopReason);
}
StringWriter stringWriter = new StringWriter();
e.printStackTrace(new PrintWriter(stringWriter));
String errorMsg = stringWriter.toString();
executeResult = new ReturnT<String>(ReturnT.FAIL_CODE, errorMsg);
XxlJobLogger.log("<br>----------- JobThread Exception:" + errorMsg + "<br>----------- xxl-job job execute end(error) -----------");
}
// callback handler info
if (!toStop) {
// commonm
TriggerCallbackThread.pushCallBack(new HandleCallbackParam(triggerParam.getLogId(), executeResult));
} else {
// is killed
ReturnT<String> stopResult = new ReturnT<String>(ReturnT.FAIL_CODE, stopReason + " [业务运行中,被强制终止]");
TriggerCallbackThread.pushCallBack(new HandleCallbackParam(triggerParam.getLogId(), stopResult));
// log filename, like "logPath/yyyy-MM-dd/9999.log"
String logFileName = XxlJobFileAppender.makeLogFileName(new Date(triggerParam.getLogDateTim()), triggerParam.getLogId());
XxlJobFileAppender.contextHolder.set(logFileName);
ShardingUtil.setShardingVo(new ShardingUtil.ShardingVO(triggerParam.getBroadcastIndex(), triggerParam.getBroadcastTotal()));
// execute
XxlJobLogger.log("<br>----------- xxl-job job execute start -----------<br>----------- Param:" + triggerParam.getExecutorParams());
executeResult = handler.execute(triggerParam.getExecutorParams());
if (executeResult == null) {
executeResult = IJobHandler.FAIL;
}
XxlJobLogger.log("<br>----------- xxl-job job execute end(finish) -----------<br>----------- ReturnT:" + executeResult);
} else {
if (idleTimes > 30) {
XxlJobExecutor.removeJobThread(jobId, "excutor idel times over limit.");
......@@ -151,16 +134,30 @@ public class JobThread extends Thread{
}
} catch (Throwable e) {
if (toStop) {
XxlJobLogger.log("<br>----------- xxl-job toStop, stopReason:" + stopReason);
XxlJobLogger.log("<br>----------- JobThread toStop, stopReason:" + stopReason);
}
StringWriter stringWriter = new StringWriter();
e.printStackTrace(new PrintWriter(stringWriter));
String errorMsg = stringWriter.toString();
XxlJobLogger.log("----------- xxl-job JobThread Exception:" + errorMsg);
}
}
executeResult = new ReturnT<String>(ReturnT.FAIL_CODE, errorMsg);
XxlJobLogger.log("<br>----------- JobThread Exception:" + errorMsg + "<br>----------- xxl-job job execute end(error) -----------");
} finally {
if(triggerParam != null) {
// callback handler info
if (!toStop) {
// commonm
TriggerCallbackThread.pushCallBack(new HandleCallbackParam(triggerParam.getLogId(), executeResult));
} else {
// is killed
ReturnT<String> stopResult = new ReturnT<String>(ReturnT.FAIL_CODE, stopReason + " [业务运行中,被强制终止]");
TriggerCallbackThread.pushCallBack(new HandleCallbackParam(triggerParam.getLogId(), stopResult));
}
}
}
}
// callback trigger request in queue
while(triggerQueue !=null && triggerQueue.size()>0){
TriggerParam triggerParam = triggerQueue.poll();
......@@ -170,7 +167,14 @@ public class JobThread extends Thread{
TriggerCallbackThread.pushCallBack(new HandleCallbackParam(triggerParam.getLogId(), stopResult));
}
}
logger.info(">>>>>>>>>>>> xxl-job JobThread stoped, hashCode:{}", Thread.currentThread());
// destroy
try {
handler.destroy();
} catch (Throwable e) {
logger.error(e.getMessage(), e);
}
logger.info(">>>>>>>>>>> xxl-job JobThread stoped, hashCode:{}", Thread.currentThread());
}
}
......@@ -40,7 +40,7 @@ public class TriggerCallbackThread {
// valid
if (XxlJobExecutor.getAdminBizList() == null) {
logger.warn(">>>>>>>>>>>> xxl-job, executor callback config fail, adminAddresses is null.");
logger.warn(">>>>>>>>>>> xxl-job, executor callback config fail, adminAddresses is null.");
return;
}
......@@ -80,7 +80,7 @@ public class TriggerCallbackThread {
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
logger.warn(">>>>>>>>>>>> xxl-job, executor callback thread destory.");
logger.info(">>>>>>>>>>> xxl-job, executor callback thread destory.");
}
});
......
......@@ -64,7 +64,7 @@ public class HttpClientUtil {
EntityUtils.consume(entity);
}
} catch (Exception e) {
logger.error("", e);
logger.error(e.getMessage(), e);
throw e;
} finally {
httpPost.releaseConnection();
......
package com.xxl.job.core.util;
import org.codehaus.jackson.JsonGenerationException;
import org.codehaus.jackson.JsonParseException;
import org.codehaus.jackson.map.JsonMappingException;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.type.TypeReference;
import com.fasterxml.jackson.core.JsonGenerationException;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
......
package com.xxl.job.core.util;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.net.ServerSocket;
/**
* net util
*
* @author xuxueli 2017-11-29 17:00:25
*/
public class NetUtil {
private static Logger logger = LoggerFactory.getLogger(NetUtil.class);
/**
* find avaliable port
*
* @param defaultPort
* @return
*/
public static int findAvailablePort(int defaultPort) {
int portTmp = defaultPort;
while (portTmp < 65535) {
if (!isPortUsed(portTmp)) {
return portTmp;
} else {
portTmp++;
}
}
portTmp = defaultPort--;
while (portTmp > 0) {
if (!isPortUsed(portTmp)) {
return portTmp;
} else {
portTmp--;
}
}
throw new IllegalStateException("no available port.");
}
/**
* check port used
*
* @param port
* @return
*/
public static boolean isPortUsed(int port) {
boolean used = false;
ServerSocket serverSocket = null;
try {
serverSocket = new ServerSocket(port);
used = false;
} catch (IOException e) {
logger.debug(">>>>>>>>>>> xxl-job, port[{}] is in use.", port);
used = true;
} finally {
if (serverSocket != null) {
try {
serverSocket.close();
} catch (IOException e) {
logger.info("");
}
}
}
return used;
}
}
package com.xxl.job.core.util;
import com.xxl.job.core.log.XxlJobFileAppender;
import org.apache.commons.exec.CommandLine;
import org.apache.commons.exec.DefaultExecutor;
import org.apache.commons.exec.PumpStreamHandler;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
......@@ -27,18 +25,6 @@ public class ScriptUtil {
* @throws IOException
*/
public static void markScriptFile(String scriptFileName, String content) throws IOException {
// filePath/
File filePathDir = new File(XxlJobFileAppender.logPath);
if (!filePathDir.exists()) {
filePathDir.mkdirs();
}
// filePath/gluesource/
File filePathSourceDir = new File(filePathDir, "gluesource");
if (!filePathSourceDir.exists()) {
filePathSourceDir.mkdirs();
}
// make file, filePath/gluesource/666-123456789.py
FileOutputStream fileOutputStream = null;
try {
......
......@@ -5,7 +5,7 @@
<parent>
<groupId>com.xuxueli</groupId>
<artifactId>xxl-job</artifactId>
<version>1.8.2-SNAPSHOT</version>
<version>1.9.0-SNAPSHOT</version>
</parent>
<artifactId>xxl-job-executor-samples</artifactId>
<packaging>pom</packaging>
......
......@@ -5,7 +5,7 @@
<parent>
<artifactId>xxl-job-executor-samples</artifactId>
<groupId>com.xuxueli</groupId>
<version>1.8.2-SNAPSHOT</version>
<version>1.9.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>xxl-job-executor-sample-jfinal</artifactId>
......@@ -16,7 +16,14 @@
<dependency>
<groupId>com.jfinal</groupId>
<artifactId>jfinal</artifactId>
<version>2.0</version>
<version>2.2</version>
</dependency>
<!-- slf4j -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>${slf4j-api.version}</version>
</dependency>
<!-- xxl-job -->
......@@ -26,11 +33,6 @@
<version>${project.parent.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>${slf4j-api.version}</version>
</dependency>
</dependencies>
</project>
\ No newline at end of file
......@@ -20,14 +20,14 @@ import java.util.concurrent.TimeUnit;
public class DemoJobHandler extends IJobHandler {
@Override
public ReturnT<String> execute(String... params) throws Exception {
public ReturnT<String> execute(String param) throws Exception {
XxlJobLogger.log("XXL-JOB, Hello World.");
for (int i = 0; i < 5; i++) {
XxlJobLogger.log("beat at:" + i);
TimeUnit.SECONDS.sleep(2);
}
return ReturnT.SUCCESS;
return SUCCESS;
}
}
......@@ -14,7 +14,7 @@ import com.xxl.job.core.util.ShardingUtil;
public class ShardingJobHandler extends IJobHandler {
@Override
public ReturnT<String> execute(String... params) throws Exception {
public ReturnT<String> execute(String param) throws Exception {
// 分片参数
ShardingUtil.ShardingVO shardingVO = ShardingUtil.getShardingVo();
......@@ -29,7 +29,7 @@ public class ShardingJobHandler extends IJobHandler {
}
}
return ReturnT.SUCCESS;
return SUCCESS;
}
}
......@@ -7,7 +7,7 @@ xxl.job.executor.ip=
xxl.job.executor.port=9997
### xxl-job log path
xxl.job.executor.logpath=/data/applogs/xxl-job/jobhandler/
xxl.job.executor.logpath=/data/applogs/xxl-job/jobhandler
### xxl-job, access token
xxl.job.accessToken=
\ No newline at end of file
......@@ -3,7 +3,11 @@
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>jfinal-demo</display-name>
<display-name>xxl-job-executor-sample-jfinal</display-name>
<context-param>
<param-name>webAppRootKey</param-name>
<param-value>xxl-job-executor-sample-jfinal</param-value>
</context-param>
<!-- jfinal -->
<filter>
......
......@@ -4,7 +4,7 @@
<parent>
<groupId>com.xuxueli</groupId>
<artifactId>xxl-job-executor-samples</artifactId>
<version>1.8.2-SNAPSHOT</version>
<version>1.9.0-SNAPSHOT</version>
</parent>
<artifactId>xxl-job-executor-sample-spring</artifactId>
<packaging>war</packaging>
......
......@@ -2,7 +2,7 @@ package com.xxl.job.executor.service.jobhandler;
import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.handler.IJobHandler;
import com.xxl.job.core.handler.annotation.JobHander;
import com.xxl.job.core.handler.annotation.JobHandler;
import com.xxl.job.core.log.XxlJobLogger;
import org.springframework.stereotype.Component;
......@@ -15,24 +15,24 @@ import java.util.concurrent.TimeUnit;
* 开发步骤:
* 1、新建一个继承com.xxl.job.core.handler.IJobHandler的Java类;
* 2、该类被Spring容器扫描为Bean实例,如加“@Component”注解;
* 3、添加 “@JobHander(value="自定义jobhandler名称")”注解,注解的value值为自定义的JobHandler名称,该名称对应的是调度中心新建任务的JobHandler属性的值。
* 3、添加 “@JobHandler(value="自定义jobhandler名称")”注解,注解的value值为自定义的JobHandler名称,该名称对应的是调度中心新建任务的JobHandler属性的值。
* 4、执行日志:需要通过 "XxlJobLogger.log" 打印执行日志;
*
* @author xuxueli 2015-12-19 19:43:36
*/
@JobHander(value="demoJobHandler")
@JobHandler(value="demoJobHandler")
@Component
public class DemoJobHandler extends IJobHandler {
@Override
public ReturnT<String> execute(String... params) throws Exception {
public ReturnT<String> execute(String param) throws Exception {
XxlJobLogger.log("XXL-JOB, Hello World.");
for (int i = 0; i < 5; i++) {
XxlJobLogger.log("beat at:" + i);
TimeUnit.SECONDS.sleep(2);
}
return ReturnT.SUCCESS;
return SUCCESS;
}
}
......@@ -2,7 +2,7 @@ package com.xxl.job.executor.service.jobhandler;
import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.handler.IJobHandler;
import com.xxl.job.core.handler.annotation.JobHander;
import com.xxl.job.core.handler.annotation.JobHandler;
import com.xxl.job.core.log.XxlJobLogger;
import com.xxl.job.core.util.ShardingUtil;
import org.springframework.stereotype.Service;
......@@ -13,12 +13,12 @@ import org.springframework.stereotype.Service;
*
* @author xuxueli 2017-07-25 20:56:50
*/
@JobHander(value="shardingJobHandler")
@JobHandler(value="shardingJobHandler")
@Service
public class ShardingJobHandler extends IJobHandler {
@Override
public ReturnT<String> execute(String... params) throws Exception {
public ReturnT<String> execute(String param) throws Exception {
// 分片参数
ShardingUtil.ShardingVO shardingVO = ShardingUtil.getShardingVo();
......@@ -33,7 +33,7 @@ public class ShardingJobHandler extends IJobHandler {
}
}
return ReturnT.SUCCESS;
return SUCCESS;
}
}
......@@ -3,9 +3,9 @@
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
http://www.springframework.org/schema/context/spring-context.xsd">
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="fileEncoding" value="utf-8" />
......
......@@ -7,7 +7,7 @@ xxl.job.executor.ip=
xxl.job.executor.port=9999
### xxl-job log path
xxl.job.executor.logpath=/data/applogs/xxl-job/jobhandler/
xxl.job.executor.logpath=/data/applogs/xxl-job/jobhandler
### xxl-job, access token
xxl.job.accessToken=
\ No newline at end of file
......@@ -3,6 +3,7 @@
xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee"
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-executor-sample-spring</display-name>
<context-param>
<param-name>webAppRootKey</param-name>
<param-value>xxl-job-executor-sample-spring</param-value>
......
......@@ -6,7 +6,7 @@
<parent>
<groupId>com.xuxueli</groupId>
<artifactId>xxl-job-executor-samples</artifactId>
<version>1.8.2-SNAPSHOT</version>
<version>1.9.0-SNAPSHOT</version>
</parent>
<artifactId>xxl-job-executor-sample-springboot</artifactId>
<packaging>jar</packaging>
......
package com.xxl.job.executor.mvc.controller;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
@EnableAutoConfiguration
public class IndexController {
@RequestMapping("/")
@ResponseBody
String index() {
return "xxl job executor running.";
}
}
\ No newline at end of file
//package com.xxl.job.executor.mvc.controller;
//
//import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
//import org.springframework.stereotype.Controller;
//import org.springframework.web.bind.annotation.RequestMapping;
//import org.springframework.web.bind.annotation.ResponseBody;
//
//@Controller
//@EnableAutoConfiguration
//public class IndexController {
//
// @RequestMapping("/")
// @ResponseBody
// String index() {
// return "xxl job executor running.";
// }
//
//}
\ No newline at end of file
......@@ -2,9 +2,9 @@ package com.xxl.job.executor.service.jobhandler;
import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.handler.IJobHandler;
import com.xxl.job.core.handler.annotation.JobHander;
import com.xxl.job.core.handler.annotation.JobHandler;
import com.xxl.job.core.log.XxlJobLogger;
import org.springframework.stereotype.Service;
import org.springframework.stereotype.Component;
import java.util.concurrent.TimeUnit;
......@@ -13,26 +13,26 @@ import java.util.concurrent.TimeUnit;
* 任务Handler的一个Demo(Bean模式)
*
* 开发步骤:
* 1、继承 “IJobHandler”
* 2、装配到Spring,例如加 “@Service” 注解;
* 3、加 “@JobHander” 注解,注解value值为新增任务生成的JobKey的值;多个JobKey用逗号分割;
* 1、新建一个继承com.xxl.job.core.handler.IJobHandler的Java类
* 2、该类被Spring容器扫描为Bean实例,如加“@Component”注解;
* 3、添加 “@JobHandler(value="自定义jobhandler名称")”注解,注解的value值为自定义的JobHandler名称,该名称对应的是调度中心新建任务的JobHandler属性的值。
* 4、执行日志:需要通过 "XxlJobLogger.log" 打印执行日志;
*
* @author xuxueli 2015-12-19 19:43:36
*/
@JobHander(value="demoJobHandler")
@Service
@JobHandler(value="demoJobHandler")
@Component
public class DemoJobHandler extends IJobHandler {
@Override
public ReturnT<String> execute(String... params) throws Exception {
public ReturnT<String> execute(String param) throws Exception {
XxlJobLogger.log("XXL-JOB, Hello World.");
for (int i = 0; i < 5; i++) {
XxlJobLogger.log("beat at:" + i);
TimeUnit.SECONDS.sleep(2);
}
return ReturnT.SUCCESS;
return SUCCESS;
}
}
......@@ -2,7 +2,7 @@ package com.xxl.job.executor.service.jobhandler;
import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.handler.IJobHandler;
import com.xxl.job.core.handler.annotation.JobHander;
import com.xxl.job.core.handler.annotation.JobHandler;
import com.xxl.job.core.log.XxlJobLogger;
import com.xxl.job.core.util.ShardingUtil;
import org.springframework.stereotype.Service;
......@@ -13,12 +13,12 @@ import org.springframework.stereotype.Service;
*
* @author xuxueli 2017-07-25 20:56:50
*/
@JobHander(value="shardingJobHandler")
@JobHandler(value="shardingJobHandler")
@Service
public class ShardingJobHandler extends IJobHandler {
@Override
public ReturnT<String> execute(String... params) throws Exception {
public ReturnT<String> execute(String param) throws Exception {
// 分片参数
ShardingUtil.ShardingVO shardingVO = ShardingUtil.getShardingVo();
......@@ -33,7 +33,7 @@ public class ShardingJobHandler extends IJobHandler {
}
}
return ReturnT.SUCCESS;
return SUCCESS;
}
}
......@@ -11,10 +11,10 @@ xxl.job.admin.addresses=http://127.0.0.1:8080/xxl-job-admin
### xxl-job executor address
xxl.job.executor.appname=xxl-job-executor-sample
xxl.job.executor.ip=
xxl.job.executor.port=9998
xxl.job.executor.port=-1
### xxl-job log path
xxl.job.executor.logpath=/data/applogs/xxl-job/jobhandler/
xxl.job.executor.logpath=/data/applogs/xxl-job/jobhandler
### xxl-job, access token
xxl.job.accessToken=
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论