Unverified 提交 60eb1909 authored 作者: thinking_fioa's avatar thinking_fioa 提交者: GitHub

Merge pull request #2 from xuxueli/master

同步xuxueli/xxl-job最新代码
<p align="center">
<a href="http://www.xuxueli.com/xxl-job/">
<img src="https://raw.githubusercontent.com/xuxueli/xxl-job/master/doc/images/xxl-logo.jpg" width="150">
</a>
<img src="https://raw.githubusercontent.com/xuxueli/xxl-job/master/doc/images/xxl-logo.jpg" width="150">
<h3 align="center">XXL-JOB</h3>
<p align="center">
XXL-JOB, a lightweight distributed task scheduling framework.
<br>
<a href="http://www.xuxueli.com/xxl-job/"><strong>-- Browse website. --</strong></a>
<a href="http://www.xuxueli.com/xxl-job/"><strong>-- Home Page --</strong></a>
<br>
<br>
<a href="https://travis-ci.org/xuxueli/xxl-job">
......@@ -71,7 +69,8 @@ XXL-JOB是一个轻量级分布式任务调度框架,其核心设计目标是
- 24、邮件报警:任务失败时支持邮件报警,支持配置多邮件地址群发报警邮件;
- 25、推送maven中央仓库: 将会把最新稳定版推送到maven中央仓库, 方便用户接入和使用;
- 26、运行报表:支持实时查看运行数据,如任务数量、调度次数、执行器数量等;以及调度报表,如调度日期分布图,调度成功分布图等;
- 27、全异步:系统底层实现全部异步化,针对密集调度进行流量削峰,理论上支持任意时长任务的运行;
- 28、国际化:调度中心支持国际化设置,提供中文、英文两种可选语言,默认为中文;
## Development
于2015年中,我在github上创建XXL-JOB项目仓库并提交第一个commit,随之进行系统结构设计,UI选型,交互设计……
......@@ -150,6 +149,13 @@ XXL-JOB是一个轻量级分布式任务调度框架,其核心设计目标是
- 57、华夏票联(北京)科技有限公司
- 58、拍拍贷
- 59、北京尚德机构在线教育有限公司
- 60、任子行股份有限公司
- 61、北京时态电子商务有限公司
- 62、深圳卷皮网络科技有限公司
- 63、北京安博通科技股份有限公司
- 64、未来无线网
- 65、厦门瓷禧网络有限公司
- 66、北京递蓝科软件股份有限公司
- ……
> 更多接入的公司,欢迎在 [登记地址](https://github.com/xuxueli/xxl-job/issues/1 ) 登记,登记仅仅为了产品推广。
......@@ -179,6 +185,6 @@ This product is open source and free, and will continue to provide free communit
## Donate
No matter how much the amount is enough to express your thought, thank you very much :) [To donate](http://www.xuxueli.com/page/donate.html )
No matter how much the donation amount is enough to express your thought, thank you very much :) [To donate](http://www.xuxueli.com/page/donate.html )
无论金额多少都足够表达您这份心意,非常感谢 :) [前往捐赠](http://www.xuxueli.com/page/donate.html )
无论捐赠金额多少都足够表达您这份心意,非常感谢 :) [前往捐赠](http://www.xuxueli.com/page/donate.html )
......@@ -12,6 +12,9 @@
### 1.1 Overview
XXL-JOB is a lightweight distributed task scheduling framework, the core design goal is to develop quickly, learning simple, lightweight, easy to expand. Is now open source and access to a number of companies online product line, download and use it now.
> English document update slightly delayed, Please check the Chinese version for the latest document.
### 1.2 Features
- 1.Simple: support through the Web page on the task CRUD operation, simple operation, a minute to get started;
- 2.Dynamic: support dynamic modification of task status, pause / resume tasks, and termination of running tasks,immediate effect;
......@@ -105,6 +108,19 @@ So far, XXL-JOB has access to a number of companies online product line, access
- 52、聚金资本
- 53、北京父母邦网络科技有限公司
- 54、中山元赫软件科技有限公司
- 55、中商惠民(北京)电子商务有限公司
- 56、凯京集团
- 57、华夏票联(北京)科技有限公司
- 58、拍拍贷
- 59、北京尚德机构在线教育有限公司
- 60、任子行股份有限公司
- 61、北京时态电子商务有限公司
- 62、深圳卷皮网络科技有限公司
- 63、北京安博通科技股份有限公司
- 64、未来无线网
- 65、厦门瓷禧网络有限公司
- 66、北京递蓝科软件股份有限公司
- 67、郑州创海软件科技公司
- ……
> The company that access and use this product is welcome to register at the [address](https://github.com/xuxueli/xxl-job/issues/1 ), only for product promotion.
......@@ -122,7 +138,7 @@ Welcome everyone's attention and use, XXL-JOB will also embrace changes, sustain
Source repository address | Release Download
--- | ---
[https://github.com/xuxueli/xxl-job](https://github.com/xuxueli/xxl-job) | [Download](https://github.com/xuxueli/xxl-job/releases)
[http://git.oschina.net/xuxueli0323/xxl-job](http://git.oschina.net/xuxueli0323/xxl-job) | [Download](http://git.oschina.net/xuxueli0323/xxl-job/releases)
[http://gitee.com/xuxueli0323/xxl-job](http://gitee.com/xuxueli0323/xxl-job) | [Download](http://gitee.com/xuxueli0323/xxl-job/releases)
#### Center repository address (The latest Release version:1.8.1)
```
......@@ -203,6 +219,9 @@ The concrete contet describe as follows:
### TOKEN used for communication between the executor and schedule center, enabled if it’s not null
xxl.job.accessToken=
### Internationalized Settings, the default is Chinese version,Switch to English when the value is "en".
xxl.job.i18n=en
#### Step 2:Deploy:
If you has finished step 1,then you can compile the project in maven and deploy the war package to tomcat.
......
差异被折叠。
......@@ -3,7 +3,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>com.xuxueli</groupId>
<artifactId>xxl-job</artifactId>
<version>1.9.0-SNAPSHOT</version>
<version>1.9.2-SNAPSHOT</version>
<packaging>pom</packaging>
<name>${project.artifactId}</name>
......@@ -20,16 +20,16 @@
<javax.servlet-api.version>3.0.1</javax.servlet-api.version>
<jsp-api.version>2.2</jsp-api.version>
<spring.version>4.3.13.RELEASE</spring.version>
<jackson.version>2.9.3</jackson.version>
<spring.version>4.3.14.RELEASE</spring.version>
<jackson.version>2.9.4</jackson.version>
<aspectjweaver.version>1.8.13</aspectjweaver.version>
<slf4j-api.version>1.7.25</slf4j-api.version>
<freemarker.version>2.3.23</freemarker.version>
<junit.version>4.12</junit.version>
<jetty-server.version>9.2.22.v20170606</jetty-server.version>
<jetty-server.version>9.2.24.v20180105</jetty-server.version>
<hessian.version>4.0.51</hessian.version>
<httpclient.version>4.5.4</httpclient.version>
<httpclient.version>4.5.5</httpclient.version>
<commons-exec.version>1.3</commons-exec.version>
<commons-collections4.version>4.1</commons-collections4.version>
......@@ -44,7 +44,7 @@
<groovy-all.version>2.4.13</groovy-all.version>
<quartz.version>2.3.0</quartz.version>
<spring-boot.version>1.5.9.RELEASE</spring-boot.version>
<spring-boot.version>1.5.10.RELEASE</spring-boot.version>
</properties>
<build>
......@@ -59,6 +59,14 @@
<encoding>UTF8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>2.6</version>
<configuration>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
......
......@@ -4,7 +4,7 @@
<parent>
<groupId>com.xuxueli</groupId>
<artifactId>xxl-job</artifactId>
<version>1.9.0-SNAPSHOT</version>
<version>1.9.2-SNAPSHOT</version>
</parent>
<artifactId>xxl-job-admin</artifactId>
<packaging>war</packaging>
......
......@@ -2,6 +2,7 @@ package com.xxl.job.admin.controller;
import com.xxl.job.admin.controller.annotation.PermessionLimit;
import com.xxl.job.admin.controller.interceptor.PermissionInterceptor;
import com.xxl.job.admin.core.util.I18nUtil;
import com.xxl.job.admin.service.XxlJobService;
import com.xxl.job.core.biz.model.ReturnT;
import org.apache.commons.lang3.StringUtils;
......@@ -40,11 +41,11 @@ public class IndexController {
return "index";
}
@RequestMapping("/triggerChartDate")
@RequestMapping("/chartInfo")
@ResponseBody
public ReturnT<Map<String, Object>> triggerChartDate(Date startDate, Date endDate) {
ReturnT<Map<String, Object>> triggerChartDate = xxlJobService.triggerChartDate(startDate, endDate);
return triggerChartDate;
public ReturnT<Map<String, Object>> chartInfo(Date startDate, Date endDate) {
ReturnT<Map<String, Object>> chartInfo = xxlJobService.chartInfo(startDate, endDate);
return chartInfo;
}
@RequestMapping("/toLogin")
......@@ -67,14 +68,14 @@ public class IndexController {
// param
if (StringUtils.isBlank(userName) || StringUtils.isBlank(password)){
return new ReturnT<String>(500, "账号或密码为空");
return new ReturnT<String>(500, I18nUtil.getString("login_param_empty"));
}
boolean ifRem = (StringUtils.isNotBlank(ifRemember) && "on".equals(ifRemember))?true:false;
// do login
boolean loginRet = PermissionInterceptor.login(response, userName, password, ifRem);
if (!loginRet) {
return new ReturnT<String>(500, "账号或密码错误");
return new ReturnT<String>(500, I18nUtil.getString("login_param_unvalid"));
}
return ReturnT.SUCCESS;
}
......
......@@ -2,6 +2,7 @@ package com.xxl.job.admin.controller;
import com.xxl.job.admin.core.model.XxlJobInfo;
import com.xxl.job.admin.core.model.XxlJobLogGlue;
import com.xxl.job.admin.core.util.I18nUtil;
import com.xxl.job.admin.dao.XxlJobInfoDao;
import com.xxl.job.admin.dao.XxlJobLogGlueDao;
import com.xxl.job.core.biz.model.ReturnT;
......@@ -34,10 +35,10 @@ public class JobCodeController {
List<XxlJobLogGlue> jobLogGlues = xxlJobLogGlueDao.findByJobId(jobId);
if (jobInfo == null) {
throw new RuntimeException("抱歉,任务不存在.");
throw new RuntimeException(I18nUtil.getString("jobinfo_glue_jobid_unvalid"));
}
if (GlueTypeEnum.BEAN == GlueTypeEnum.match(jobInfo.getGlueType())) {
throw new RuntimeException("该任务非GLUE模式.");
throw new RuntimeException(I18nUtil.getString("jobinfo_glue_gluetype_unvalid"));
}
// Glue类型-字典
......@@ -53,14 +54,14 @@ public class JobCodeController {
public ReturnT<String> save(Model model, int id, String glueSource, String glueRemark) {
// valid
if (glueRemark==null) {
return new ReturnT<String>(500, "请输入备注");
return new ReturnT<String>(500, (I18nUtil.getString("system_please_input") + I18nUtil.getString("jobinfo_glue_remark")) );
}
if (glueRemark.length()<4 || glueRemark.length()>100) {
return new ReturnT<String>(500, "备注长度应该在4至100之间");
return new ReturnT<String>(500, I18nUtil.getString("jobinfo_glue_remark_limit"));
}
XxlJobInfo exists_jobInfo = xxlJobInfoDao.loadById(id);
if (exists_jobInfo == null) {
return new ReturnT<String>(500, "参数异常");
return new ReturnT<String>(500, I18nUtil.getString("jobinfo_glue_jobid_unvalid"));
}
// update new code
......
package com.xxl.job.admin.controller;
import com.xxl.job.admin.core.model.XxlJobGroup;
import com.xxl.job.admin.core.util.I18nUtil;
import com.xxl.job.admin.dao.XxlJobGroupDao;
import com.xxl.job.admin.dao.XxlJobInfoDao;
import com.xxl.job.core.biz.model.ReturnT;
......@@ -42,22 +43,22 @@ public class JobGroupController {
// valid
if (xxlJobGroup.getAppName()==null || StringUtils.isBlank(xxlJobGroup.getAppName())) {
return new ReturnT<String>(500, "请输入AppName");
return new ReturnT<String>(500, (I18nUtil.getString("system_please_input")+"AppName") );
}
if (xxlJobGroup.getAppName().length()>64) {
return new ReturnT<String>(500, "AppName长度限制为4~64");
if (xxlJobGroup.getAppName().length()<4 || xxlJobGroup.getAppName().length()>64) {
return new ReturnT<String>(500, I18nUtil.getString("jobgroup_field_appName_length") );
}
if (xxlJobGroup.getTitle()==null || StringUtils.isBlank(xxlJobGroup.getTitle())) {
return new ReturnT<String>(500, "请输入名称");
return new ReturnT<String>(500, (I18nUtil.getString("system_please_input") + I18nUtil.getString("jobgroup_field_title")) );
}
if (xxlJobGroup.getAddressType()!=0) {
if (StringUtils.isBlank(xxlJobGroup.getAddressList())) {
return new ReturnT<String>(500, "手动录入注册方式,机器地址不可为空");
return new ReturnT<String>(500, I18nUtil.getString("jobgroup_field_addressType_limit") );
}
String[] addresss = xxlJobGroup.getAddressList().split(",");
for (String item: addresss) {
if (StringUtils.isBlank(item)) {
return new ReturnT<String>(500, "机器地址非法");
return new ReturnT<String>(500, I18nUtil.getString("jobgroup_field_registryList_unvalid") );
}
}
}
......@@ -71,22 +72,22 @@ public class JobGroupController {
public ReturnT<String> update(XxlJobGroup xxlJobGroup){
// valid
if (xxlJobGroup.getAppName()==null || StringUtils.isBlank(xxlJobGroup.getAppName())) {
return new ReturnT<String>(500, "请输入AppName");
return new ReturnT<String>(500, (I18nUtil.getString("system_please_input")+"AppName") );
}
if (xxlJobGroup.getAppName().length()>64) {
return new ReturnT<String>(500, "AppName长度限制为4~64");
if (xxlJobGroup.getAppName().length()<4 || xxlJobGroup.getAppName().length()>64) {
return new ReturnT<String>(500, I18nUtil.getString("jobgroup_field_appName_length") );
}
if (xxlJobGroup.getTitle()==null || StringUtils.isBlank(xxlJobGroup.getTitle())) {
return new ReturnT<String>(500, "请输入名称");
return new ReturnT<String>(500, (I18nUtil.getString("system_please_input") + I18nUtil.getString("jobgroup_field_title")) );
}
if (xxlJobGroup.getAddressType()!=0) {
if (StringUtils.isBlank(xxlJobGroup.getAddressList())) {
return new ReturnT<String>(500, "手动录入注册方式,机器地址不可为空");
return new ReturnT<String>(500, I18nUtil.getString("jobgroup_field_addressType_limit") );
}
String[] addresss = xxlJobGroup.getAddressList().split(",");
for (String item: addresss) {
if (StringUtils.isBlank(item)) {
return new ReturnT<String>(500, "机器地址非法");
return new ReturnT<String>(500, I18nUtil.getString("jobgroup_field_registryList_unvalid") );
}
}
}
......@@ -100,14 +101,14 @@ public class JobGroupController {
public ReturnT<String> remove(int id){
// valid
int count = xxlJobInfoDao.pageListCount(0, 10, id, null);
int count = xxlJobInfoDao.pageListCount(0, 10, id, null, null);
if (count > 0) {
return new ReturnT<String>(500, "该分组使用中, 不可删除");
return new ReturnT<String>(500, I18nUtil.getString("jobgroup_del_limit_0") );
}
List<XxlJobGroup> allList = xxlJobGroupDao.findAll();
if (allList.size() == 1) {
return new ReturnT<String>(500, "删除失败, 系统需要至少预留一个默认分组");
return new ReturnT<String>(500, I18nUtil.getString("jobgroup_del_limit_1") );
}
int ret = xxlJobGroupDao.remove(id);
......
......@@ -53,9 +53,9 @@ public class JobInfoController {
@ResponseBody
public Map<String, Object> pageList(@RequestParam(required = false, defaultValue = "0") int start,
@RequestParam(required = false, defaultValue = "10") int length,
int jobGroup, String executorHandler, String filterTime) {
int jobGroup, String jobDesc, String executorHandler, String filterTime) {
return xxlJobService.pageList(start, length, jobGroup, executorHandler, filterTime);
return xxlJobService.pageList(start, length, jobGroup, jobDesc, executorHandler, filterTime);
}
@RequestMapping("/add")
......@@ -64,10 +64,10 @@ public class JobInfoController {
return xxlJobService.add(jobInfo);
}
@RequestMapping("/reschedule")
@RequestMapping("/update")
@ResponseBody
public ReturnT<String> reschedule(XxlJobInfo jobInfo) {
return xxlJobService.reschedule(jobInfo);
public ReturnT<String> update(XxlJobInfo jobInfo) {
return xxlJobService.update(jobInfo);
}
@RequestMapping("/remove")
......
......@@ -4,12 +4,14 @@ 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.I18nUtil;
import com.xxl.job.admin.dao.XxlJobGroupDao;
import com.xxl.job.admin.dao.XxlJobInfoDao;
import com.xxl.job.admin.dao.XxlJobLogDao;
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.glue.GlueTypeEnum;
import com.xxl.job.core.rpc.netcom.NetComClientProxy;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateUtils;
......@@ -50,6 +52,7 @@ public class JobLogController {
// 执行器列表
List<XxlJobGroup> jobGroupList = xxlJobGroupDao.findAll();
model.addAttribute("JobGroupList", jobGroupList);
model.addAttribute("GlueTypeEnum", GlueTypeEnum.values());
// 任务
if (jobId > 0) {
......@@ -105,7 +108,7 @@ public class JobLogController {
ReturnT<String> logStatue = ReturnT.SUCCESS;
XxlJobLog jobLog = xxlJobLogDao.load(id);
if (jobLog == null) {
throw new RuntimeException("抱歉,日志ID非法.");
throw new RuntimeException(I18nUtil.getString("joblog_logid_unvalid"));
}
model.addAttribute("triggerCode", jobLog.getTriggerCode());
......@@ -145,10 +148,10 @@ public class JobLogController {
XxlJobLog log = xxlJobLogDao.load(id);
XxlJobInfo jobInfo = xxlJobInfoDao.loadById(log.getJobId());
if (jobInfo==null) {
return new ReturnT<String>(500, "参数异常");
return new ReturnT<String>(500, I18nUtil.getString("jobinfo_glue_jobid_unvalid"));
}
if (ReturnT.SUCCESS_CODE != log.getTriggerCode()) {
return new ReturnT<String>(500, "调度失败,无法终止日志");
return new ReturnT<String>(500, I18nUtil.getString("joblog_kill_log_limit"));
}
// request of kill
......@@ -163,7 +166,7 @@ public class JobLogController {
if (ReturnT.SUCCESS_CODE == runResult.getCode()) {
log.setHandleCode(ReturnT.FAIL_CODE);
log.setHandleMsg("人为操作主动终止:" + (runResult.getMsg()!=null?runResult.getMsg():""));
log.setHandleMsg( I18nUtil.getString("joblog_kill_log_byman")+":" + (runResult.getMsg()!=null?runResult.getMsg():""));
log.setHandleTime(new Date());
xxlJobLogDao.updateHandleInfo(log);
return new ReturnT<String>(runResult.getMsg());
......@@ -197,7 +200,7 @@ public class JobLogController {
} else if (type == 9) {
clearBeforeNum = 0; // 清理所有日志数据
} else {
return new ReturnT<String>(ReturnT.FAIL_CODE, "清理类型参数异常");
return new ReturnT<String>(ReturnT.FAIL_CODE, I18nUtil.getString("joblog_clean_type_unvalid"));
}
xxlJobLogDao.clearLog(jobGroup, jobId, clearBeforeTime, clearBeforeNum);
......
package com.xxl.job.admin.controller.interceptor;
import com.xxl.job.admin.core.util.FtlUtil;
import com.xxl.job.admin.core.util.I18nUtil;
import org.apache.commons.lang3.ArrayUtils;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
......@@ -19,7 +21,8 @@ public class CookieInterceptor extends HandlerInterceptorAdapter {
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
// cookie
if (modelAndView!=null && ArrayUtils.isNotEmpty(request.getCookies())) {
HashMap<String, Cookie> cookieMap = new HashMap<String, Cookie>();
for (Cookie ck : request.getCookies()) {
......@@ -27,6 +30,11 @@ public class CookieInterceptor extends HandlerInterceptorAdapter {
}
modelAndView.addObject("cookieMap", cookieMap);
}
// static method
if (modelAndView != null) {
modelAndView.addObject("I18nUtil", FtlUtil.generateStaticModel(I18nUtil.class.getName()));
}
super.postHandle(request, response, handler, modelAndView);
}
......
package com.xxl.job.admin.controller.interceptor;
import com.xxl.job.admin.controller.annotation.PermessionLimit;
import com.xxl.job.admin.core.conf.XxlJobAdminConfig;
import com.xxl.job.admin.core.util.CookieUtil;
import com.xxl.job.admin.core.util.PropertiesUtil;
import org.apache.commons.codec.digest.DigestUtils;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
......@@ -22,8 +22,8 @@ public class PermissionInterceptor extends HandlerInterceptorAdapter {
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");
String password = PropertiesUtil.getString("xxl.job.login.password");
String username = XxlJobAdminConfig.getAdminConfig().getLoginUsername();
String password = XxlJobAdminConfig.getAdminConfig().getLoginPassword();
// login token
String tokenTmp = DigestUtils.md5Hex(username + "_" + password);
......
......@@ -11,6 +11,7 @@ import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* common exception resolver
......@@ -23,19 +24,34 @@ public class WebExceptionResolver implements HandlerExceptionResolver {
public ModelAndView resolveException(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex) {
logger.error("WebExceptionResolver:{}", ex);
ModelAndView mv = new ModelAndView();
// if json
boolean isJson = false;
HandlerMethod method = (HandlerMethod)handler;
ResponseBody responseBody = method.getMethodAnnotation(ResponseBody.class);
if (responseBody != null) {
response.setContentType("application/json;charset=UTF-8");
mv.addObject("result", JacksonUtil.writeValueAsString(new ReturnT<String>(500, ex.toString().replaceAll("\n", "<br/>"))));
mv.setViewName("/common/common.result");
isJson = true;
}
// error result
ReturnT<String> errorResult = new ReturnT<String>(ReturnT.FAIL_CODE, ex.toString().replaceAll("\n", "<br/>"));
// response
ModelAndView mv = new ModelAndView();
if (isJson) {
try {
response.setContentType("application/json;charset=utf-8");
response.getWriter().print(JacksonUtil.writeValueAsString(errorResult));
} catch (IOException e) {
logger.error(e.getMessage(), e);
}
return mv;
} else {
mv.addObject("exceptionMsg", ex.toString().replaceAll("\n", "<br/>"));
mv.addObject("exceptionMsg", errorResult.getMsg());
mv.setViewName("/common/common.exception");
return mv;
}
return mv;
}
}
\ No newline at end of file
package com.xxl.job.admin.core.conf;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
/**
* xxl-job config
*
* @author xuxueli 2017-04-28
*/
@Configuration
public class XxlJobAdminConfig implements InitializingBean{
private static XxlJobAdminConfig adminConfig = null;
public static XxlJobAdminConfig getAdminConfig() {
return adminConfig;
}
@Override
public void afterPropertiesSet() throws Exception {
adminConfig = this;
}
@Value("${xxl.job.mail.host}")
private String mailHost;
@Value("${xxl.job.mail.port}")
private String mailPort;
@Value("${xxl.job.mail.username}")
private String mailUsername;
@Value("${xxl.job.mail.password}")
private String mailPassword;
@Value("${xxl.job.mail.sendNick}")
private String mailSendNick;
@Value("${xxl.job.login.username}")
private String loginUsername;
@Value("${xxl.job.login.password}")
private String loginPassword;
@Value("${xxl.job.i18n}")
private String i18n;
public String getMailHost() {
return mailHost;
}
public String getMailPort() {
return mailPort;
}
public String getMailUsername() {
return mailUsername;
}
public String getMailPassword() {
return mailPassword;
}
public String getMailSendNick() {
return mailSendNick;
}
public String getLoginUsername() {
return loginUsername;
}
public String getLoginPassword() {
return loginPassword;
}
public String getI18n() {
return i18n;
}
}
package com.xxl.job.admin.core.enums;
import com.xxl.job.admin.core.util.I18nUtil;
/**
* Created by xuxueli on 17/5/9.
*/
public enum ExecutorFailStrategyEnum {
FAIL_ALARM("失败告警"),
FAIL_ALARM(I18nUtil.getString("jobconf_fail_alarm")),
FAIL_RETRY("失败重试");
FAIL_RETRY(I18nUtil.getString("jobconf_fail_retry"));
private final String title;
private ExecutorFailStrategyEnum(String title) {
......
package com.xxl.job.admin.core.route;
import com.xxl.job.admin.core.route.strategy.*;
import com.xxl.job.admin.core.util.I18nUtil;
/**
* Created by xuxueli on 17/3/10.
*/
public enum ExecutorRouteStrategyEnum {
FIRST("第一个", new ExecutorRouteFirst()),
LAST("最后一个", new ExecutorRouteLast()),
ROUND("轮询", new ExecutorRouteRound()),
RANDOM("随机", new ExecutorRouteRandom()),
CONSISTENT_HASH("一致性HASH", new ExecutorRouteConsistentHash()),
LEAST_FREQUENTLY_USED("最不经常使用", new ExecutorRouteLFU()),
LEAST_RECENTLY_USED("最近最久未使用", new ExecutorRouteLRU()),
FAILOVER("故障转移", new ExecutorRouteFailover()),
BUSYOVER("忙碌转移", new ExecutorRouteBusyover()),
SHARDING_BROADCAST("分片广播", null);
FIRST(I18nUtil.getString("jobconf_route_first"), new ExecutorRouteFirst()),
LAST(I18nUtil.getString("jobconf_route_last"), new ExecutorRouteLast()),
ROUND(I18nUtil.getString("jobconf_route_round"), new ExecutorRouteRound()),
RANDOM(I18nUtil.getString("jobconf_route_random"), new ExecutorRouteRandom()),
CONSISTENT_HASH(I18nUtil.getString("jobconf_route_consistenthash"), new ExecutorRouteConsistentHash()),
LEAST_FREQUENTLY_USED(I18nUtil.getString("jobconf_route_lfu"), new ExecutorRouteLFU()),
LEAST_RECENTLY_USED(I18nUtil.getString("jobconf_route_lru"), new ExecutorRouteLRU()),
FAILOVER(I18nUtil.getString("jobconf_route_failover"), new ExecutorRouteFailover()),
BUSYOVER(I18nUtil.getString("jobconf_route_busyover"), new ExecutorRouteBusyover()),
SHARDING_BROADCAST(I18nUtil.getString("jobconf_route_shard"), null);
ExecutorRouteStrategyEnum(String title, ExecutorRouter router) {
this.title = title;
......
......@@ -3,6 +3,7 @@ package com.xxl.job.admin.core.route.strategy;
import com.xxl.job.admin.core.route.ExecutorRouter;
import com.xxl.job.admin.core.schedule.XxlJobDynamicScheduler;
import com.xxl.job.admin.core.trigger.XxlJobTrigger;
import com.xxl.job.admin.core.util.I18nUtil;
import com.xxl.job.core.biz.ExecutorBiz;
import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.biz.model.TriggerParam;
......@@ -33,7 +34,7 @@ public class ExecutorRouteBusyover extends ExecutorRouter {
idleBeatResult = new ReturnT<String>(ReturnT.FAIL_CODE, ""+e );
}
idleBeatResultSB.append( (idleBeatResultSB.length()>0)?"<br><br>":"")
.append("空闲检测:")
.append(I18nUtil.getString("jobconf_idleBeat") + ":")
.append("<br>address:").append(address)
.append("<br>code:").append(idleBeatResult.getCode())
.append("<br>msg:").append(idleBeatResult.getMsg());
......
......@@ -3,6 +3,7 @@ package com.xxl.job.admin.core.route.strategy;
import com.xxl.job.admin.core.route.ExecutorRouter;
import com.xxl.job.admin.core.schedule.XxlJobDynamicScheduler;
import com.xxl.job.admin.core.trigger.XxlJobTrigger;
import com.xxl.job.admin.core.util.I18nUtil;
import com.xxl.job.core.biz.ExecutorBiz;
import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.biz.model.TriggerParam;
......@@ -33,7 +34,7 @@ public class ExecutorRouteFailover extends ExecutorRouter {
beatResult = new ReturnT<String>(ReturnT.FAIL_CODE, ""+e );
}
beatResultSB.append( (beatResultSB.length()>0)?"<br><br>":"")
.append("心跳检测:")
.append(I18nUtil.getString("jobconf_beat") + ":")
.append("<br>address:").append(address)
.append("<br>code:").append(beatResult.getCode())
.append("<br>msg:").append(beatResult.getMsg());
......
......@@ -4,6 +4,7 @@ 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.I18nUtil;
import com.xxl.job.admin.core.util.MailUtil;
import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.handler.IJobHandler;
......@@ -119,14 +120,14 @@ public class JobFailMonitorHelper {
// ---------------------- alarm ----------------------
// email alarm template
private static final String mailBodyTemplate = "<h5>监控告警明细:</span>" +
private static final String mailBodyTemplate = "<h5>" + I18nUtil.getString("jobconf_monitor_detail") + ":</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>任务ID</td>\n" +
" <td>任务描述</td>\n" +
" <td>告警类型</td>\n" +
" <td>"+ I18nUtil.getString("jobinfo_field_jobgroup") +"</td>\n" +
" <td>"+ I18nUtil.getString("jobinfo_field_id") +"</td>\n" +
" <td>"+ I18nUtil.getString("jobinfo_field_jobdesc") +"</td>\n" +
" <td>"+ I18nUtil.getString("jobconf_monitor_alarm_title") +"</td>\n" +
" </tr>\n" +
" <thead/>\n" +
" <tbody>\n" +
......@@ -134,7 +135,7 @@ public class JobFailMonitorHelper {
" <td>{0}</td>\n" +
" <td>{1}</td>\n" +
" <td>{2}</td>\n" +
" <td>调度失败</td>\n" +
" <td>"+ I18nUtil.getString("jobconf_monitor_alarm_type") +"</td>\n" +
" </tr>\n" +
" <tbody>\n" +
"</table>";
......@@ -154,7 +155,7 @@ public class JobFailMonitorHelper {
for (String email: emailSet) {
XxlJobGroup group = XxlJobDynamicScheduler.xxlJobGroupDao.load(Integer.valueOf(info.getJobGroup()));
String title = "调度中心监控报警";
String title = I18nUtil.getString("jobconf_monitor");
String content = MessageFormat.format(mailBodyTemplate, group!=null?group.getTitle():"null", info.getId(), info.getJobDesc());
MailUtil.sendMail(email, title, content);
......
......@@ -7,6 +7,7 @@ import com.xxl.job.admin.core.model.XxlJobLog;
import com.xxl.job.admin.core.route.ExecutorRouteStrategyEnum;
import com.xxl.job.admin.core.schedule.XxlJobDynamicScheduler;
import com.xxl.job.admin.core.thread.JobFailMonitorHelper;
import com.xxl.job.admin.core.util.I18nUtil;
import com.xxl.job.core.biz.ExecutorBiz;
import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.biz.model.TriggerParam;
......@@ -67,17 +68,18 @@ public class XxlJobTrigger {
ReturnT<String> triggerResult = new ReturnT<String>(null);
StringBuffer triggerMsgSb = new StringBuffer();
triggerMsgSb.append("调度机器:").append(IpUtil.getIp());
triggerMsgSb.append("<br>执行器-注册方式:").append( (group.getAddressType() == 0)?"自动注册":"手动录入" );
triggerMsgSb.append("<br>执行器-地址列表:").append(group.getRegistryList());
triggerMsgSb.append("<br>路由策略:").append(executorRouteStrategyEnum.getTitle()).append("("+i+"/"+addressList.size()+")"); // update01
triggerMsgSb.append("<br>阻塞处理策略:").append(blockStrategy.getTitle());
triggerMsgSb.append("<br>失败处理策略:").append(failStrategy.getTitle());
triggerMsgSb.append(I18nUtil.getString("jobconf_trigger_admin_adress")).append(":").append(IpUtil.getIp());
triggerMsgSb.append("<br>").append(I18nUtil.getString("jobconf_trigger_exe_regtype")).append(":")
.append( (group.getAddressType() == 0)?I18nUtil.getString("jobgroup_field_addressType_0"):I18nUtil.getString("jobgroup_field_addressType_1") );
triggerMsgSb.append("<br>").append(I18nUtil.getString("jobconf_trigger_exe_regaddress")).append(":").append(group.getRegistryList());
triggerMsgSb.append("<br>").append(I18nUtil.getString("jobinfo_field_executorRouteStrategy")).append(":").append(executorRouteStrategyEnum.getTitle()).append("("+i+"/"+addressList.size()+")"); // update01
triggerMsgSb.append("<br>").append(I18nUtil.getString("jobinfo_field_executorBlockStrategy")).append(":").append(blockStrategy.getTitle());
triggerMsgSb.append("<br>").append(I18nUtil.getString("jobinfo_field_executorFailStrategy")).append(":").append(failStrategy.getTitle());
// 3、trigger-valid
if (triggerResult.getCode()==ReturnT.SUCCESS_CODE && CollectionUtils.isEmpty(addressList)) {
triggerResult.setCode(ReturnT.FAIL_CODE);
triggerMsgSb.append("<br>----------------------<br>").append("调度失败:").append("执行器地址为空");
triggerMsgSb.append("<br>----------------------<br>").append(I18nUtil.getString("jobconf_trigger_address_empty"));
}
if (triggerResult.getCode() == ReturnT.SUCCESS_CODE) {
......@@ -97,12 +99,12 @@ public class XxlJobTrigger {
// 4.2、trigger-run (route run / trigger remote executor)
triggerResult = runExecutor(triggerParam, address); // update03
triggerMsgSb.append("<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>").append(triggerResult.getMsg());
triggerMsgSb.append("<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>"+ I18nUtil.getString("jobconf_trigger_run") +"<<<<<<<<<<< </span><br>").append(triggerResult.getMsg());
// 4.3、trigger (fail retry)
if (triggerResult.getCode()!=ReturnT.SUCCESS_CODE && failStrategy == ExecutorFailStrategyEnum.FAIL_RETRY) {
triggerResult = runExecutor(triggerParam, address); // update04
triggerMsgSb.append("<br><br><span style=\"color:#F39C12;\" > >>>>>>>>>>>失败重试<<<<<<<<<<< </span><br>").append(triggerResult.getMsg());
triggerMsgSb.append("<br><br><span style=\"color:#F39C12;\" > >>>>>>>>>>>"+ I18nUtil.getString("jobconf_trigger_fail_retry") +"<<<<<<<<<<< </span><br>").append(triggerResult.getMsg());
}
}
......@@ -112,7 +114,7 @@ public class XxlJobTrigger {
jobLog.setTriggerMsg(triggerMsgSb.toString());
XxlJobDynamicScheduler.xxlJobLogDao.updateTriggerInfo(jobLog);
// 6、monitor triger
// 6、monitor trigger
JobFailMonitorHelper.monitor(jobLog.getId());
logger.debug(">>>>>>>>>>> xxl-job trigger end, jobId:{}", jobLog.getId());
......@@ -134,17 +136,18 @@ public class XxlJobTrigger {
ReturnT<String> triggerResult = new ReturnT<String>(null);
StringBuffer triggerMsgSb = new StringBuffer();
triggerMsgSb.append("调度机器:").append(IpUtil.getIp());
triggerMsgSb.append("<br>执行器-注册方式:").append( (group.getAddressType() == 0)?"自动注册":"手动录入" );
triggerMsgSb.append("<br>执行器-地址列表:").append(group.getRegistryList());
triggerMsgSb.append("<br>路由策略:").append(executorRouteStrategyEnum.getTitle());
triggerMsgSb.append("<br>阻塞处理策略:").append(blockStrategy.getTitle());
triggerMsgSb.append("<br>失败处理策略:").append(failStrategy.getTitle());
triggerMsgSb.append(I18nUtil.getString("jobconf_trigger_admin_adress")).append(":").append(IpUtil.getIp());
triggerMsgSb.append("<br>").append(I18nUtil.getString("jobconf_trigger_exe_regtype")).append(":")
.append( (group.getAddressType() == 0)?I18nUtil.getString("jobgroup_field_addressType_0"):I18nUtil.getString("jobgroup_field_addressType_1") );
triggerMsgSb.append("<br>").append(I18nUtil.getString("jobconf_trigger_exe_regaddress")).append(":").append(group.getRegistryList());
triggerMsgSb.append("<br>").append(I18nUtil.getString("jobinfo_field_executorRouteStrategy")).append(":").append(executorRouteStrategyEnum.getTitle());
triggerMsgSb.append("<br>").append(I18nUtil.getString("jobinfo_field_executorBlockStrategy")).append(":").append(blockStrategy.getTitle());
triggerMsgSb.append("<br>").append(I18nUtil.getString("jobinfo_field_executorFailStrategy")).append(":").append(failStrategy.getTitle());
// 3、trigger-valid
if (triggerResult.getCode()==ReturnT.SUCCESS_CODE && CollectionUtils.isEmpty(addressList)) {
triggerResult.setCode(ReturnT.FAIL_CODE);
triggerMsgSb.append("<br>----------------------<br>").append("调度失败:").append("执行器地址为空");
triggerMsgSb.append("<br>----------------------<br>").append(I18nUtil.getString("jobconf_trigger_address_empty"));
}
if (triggerResult.getCode() == ReturnT.SUCCESS_CODE) {
......@@ -164,12 +167,12 @@ public class XxlJobTrigger {
// 4.2、trigger-run (route run / trigger remote executor)
triggerResult = executorRouteStrategyEnum.getRouter().routeRun(triggerParam, addressList);
triggerMsgSb.append("<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>").append(triggerResult.getMsg());
triggerMsgSb.append("<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>"+ I18nUtil.getString("jobconf_trigger_run") +"<<<<<<<<<<< </span><br>").append(triggerResult.getMsg());
// 4.3、trigger (fail retry)
if (triggerResult.getCode()!=ReturnT.SUCCESS_CODE && failStrategy == ExecutorFailStrategyEnum.FAIL_RETRY) {
triggerResult = executorRouteStrategyEnum.getRouter().routeRun(triggerParam, addressList);
triggerMsgSb.append("<br><br><span style=\"color:#F39C12;\" > >>>>>>>>>>>调度失败重试<<<<<<<<<<< </span><br>").append(triggerResult.getMsg());
triggerMsgSb.append("<br><br><span style=\"color:#F39C12;\" > >>>>>>>>>>>"+ I18nUtil.getString("jobconf_trigger_fail_retry") +"<<<<<<<<<<< </span><br>").append(triggerResult.getMsg());
}
}
......@@ -179,7 +182,7 @@ public class XxlJobTrigger {
jobLog.setTriggerMsg(triggerMsgSb.toString());
XxlJobDynamicScheduler.xxlJobLogDao.updateTriggerInfo(jobLog);
// 6、monitor triger
// 6、monitor trigger
JobFailMonitorHelper.monitor(jobLog.getId());
logger.debug(">>>>>>>>>>> xxl-job trigger end, jobId:{}", jobLog.getId());
}
......@@ -202,7 +205,7 @@ public class XxlJobTrigger {
runResult = new ReturnT<String>(ReturnT.FAIL_CODE, ""+e );
}
StringBuffer runResultSB = new StringBuffer("触发调度:");
StringBuffer runResultSB = new StringBuffer(I18nUtil.getString("jobconf_trigger_run") + ":");
runResultSB.append("<br>address:").append(address);
runResultSB.append("<br>code:").append(runResult.getCode());
runResultSB.append("<br>msg:").append(runResult.getMsg());
......
package com.xxl.job.admin.core.util;
import freemarker.ext.beans.BeansWrapper;
import freemarker.template.TemplateHashModel;
/**
* ftl util
*
* @author xuxueli 2018-01-17 20:37:48
*/
public class FtlUtil {
public static TemplateHashModel generateStaticModel(String packageName) {
try {
BeansWrapper wrapper = BeansWrapper.getDefaultInstance();
TemplateHashModel staticModels = wrapper.getStaticModels();
TemplateHashModel fileStatics = (TemplateHashModel) staticModels.get(packageName);
return fileStatics;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
package com.xxl.job.admin.core.util;
import com.xxl.job.admin.core.conf.XxlJobAdminConfig;
import com.xxl.job.core.util.JacksonUtil;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.EncodedResource;
import org.springframework.core.io.support.PropertiesLoaderUtils;
import java.io.IOException;
import java.text.MessageFormat;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
/**
* i18n util
*
* @author xuxueli 2018-01-17 20:39:06
*/
public class I18nUtil {
private static Logger logger = LoggerFactory.getLogger(I18nUtil.class);
private static Properties prop = null;
public static Properties loadI18nProp(){
if (prop != null) {
return prop;
}
try {
// bild i18n prop
String i18n = XxlJobAdminConfig.getAdminConfig().getI18n();
i18n = StringUtils.isNotBlank(i18n)?("_"+i18n):i18n;
String i18nFile = MessageFormat.format("i18n/message{0}.properties", i18n);
// load prop
Resource resource = new ClassPathResource(i18nFile);
EncodedResource encodedResource = new EncodedResource(resource,"UTF-8");
prop = PropertiesLoaderUtils.loadProperties(encodedResource);
} catch (IOException e) {
logger.error(e.getMessage(), e);
}
return prop;
}
/**
* get val of i18n key
*
* @param key
* @return
*/
public static String getString(String key) {
return loadI18nProp().getProperty(key);
}
/**
* get mult val of i18n mult key, as json
*
* @param keys
* @return
*/
public static String getMultString(String... keys) {
Map<String, String> map = new HashMap<>();
Properties prop = loadI18nProp();
if (keys!=null && keys.length>0) {
for (String key: keys) {
map.put(key, prop.getProperty(key));
}
} else {
for (String key: prop.stringPropertyNames()) {
map.put(key, prop.getProperty(key));
}
}
String json = JacksonUtil.writeValueAsString(map);
return json;
}
}
package com.xxl.job.admin.core.util;
import org.apache.commons.lang3.StringUtils;
import java.util.concurrent.ConcurrentHashMap;
/**
* local cache tool
*
* @author xuxueli 2018-01-22 21:37:34
*/
public class LocalCacheUtil {
private static ConcurrentHashMap<String, LocalCacheData> cacheRepository = new ConcurrentHashMap<>();
private static class LocalCacheData{
private String key;
private Object val;
private long timeoutTime;
public LocalCacheData() {
}
public LocalCacheData(String key, Object val, long timeoutTime) {
this.key = key;
this.val = val;
this.timeoutTime = timeoutTime;
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public Object getVal() {
return val;
}
public void setVal(Object val) {
this.val = val;
}
public long getTimeoutTime() {
return timeoutTime;
}
public void setTimeoutTime(long timeoutTime) {
this.timeoutTime = timeoutTime;
}
}
/**
* set cache
*
* @param key
* @param val
* @param cacheTime
* @return
*/
public static boolean set(String key, Object val, long cacheTime){
// clean timeout cache, before set new cache (avoid cache too much)
cleanTimeutCache();
// set new cache
if (StringUtils.isBlank(key)) {
return false;
}
if (val == null) {
remove(key);
}
if (cacheTime <= 0) {
remove(key);
}
long timeoutTime = System.currentTimeMillis() + cacheTime;
LocalCacheData localCacheData = new LocalCacheData(key, val, timeoutTime);
cacheRepository.put(localCacheData.getKey(), localCacheData);
return true;
}
/**
* remove cache
*
* @param key
* @return
*/
public static boolean remove(String key){
if (StringUtils.isBlank(key)) {
return false;
}
cacheRepository.remove(key);
return true;
}
/**
* get cache
*
* @param key
* @return
*/
public static Object get(String key){
if (StringUtils.isBlank(key)) {
return null;
}
LocalCacheData localCacheData = cacheRepository.get(key);
if (localCacheData!=null && System.currentTimeMillis()<localCacheData.getTimeoutTime()) {
return localCacheData.getVal();
} else {
remove(key);
return null;
}
}
/**
* clean timeout cache
*
* @return
*/
public static boolean cleanTimeutCache(){
if (!cacheRepository.keySet().isEmpty()) {
for (String key: cacheRepository.keySet()) {
LocalCacheData localCacheData = cacheRepository.get(key);
if (localCacheData!=null && System.currentTimeMillis()>=localCacheData.getTimeoutTime()) {
cacheRepository.remove(key);
}
}
}
return true;
}
}
package com.xxl.job.admin.core.util;
import com.xxl.job.admin.core.conf.XxlJobAdminConfig;
import org.apache.commons.mail.DefaultAuthenticator;
import org.apache.commons.mail.EmailException;
import org.apache.commons.mail.HtmlEmail;
......@@ -16,19 +17,6 @@ import java.nio.charset.Charset;
public class MailUtil {
private static Logger logger = LoggerFactory.getLogger(MailUtil.class);
private static String host;
private static String port;
private static String username;
private static String password;
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");
sendNick = PropertiesUtil.getString("xxl.job.mail.sendNick");
}
/**
*
* @param toAddress 收件人邮箱
......@@ -46,13 +34,13 @@ public class MailUtil {
//email.setTLS(true); // 是否TLS校验,,某些邮箱需要TLS安全校验,同理有SSL校验
//email.setSSL(true);
email.setHostName(host);
email.setSmtpPort(Integer.valueOf(port));
email.setHostName(XxlJobAdminConfig.getAdminConfig().getMailHost());
email.setSmtpPort(Integer.valueOf(XxlJobAdminConfig.getAdminConfig().getMailPort()));
//email.setSslSmtpPort(port);
email.setAuthenticator(new DefaultAuthenticator(username, password));
email.setAuthenticator(new DefaultAuthenticator(XxlJobAdminConfig.getAdminConfig().getMailUsername(), XxlJobAdminConfig.getAdminConfig().getMailPassword()));
email.setCharset(Charset.defaultCharset().name());
email.setFrom(username, sendNick);
email.setFrom(XxlJobAdminConfig.getAdminConfig().getMailUsername(), XxlJobAdminConfig.getAdminConfig().getMailSendNick());
email.addTo(toAddress);
email.setSubject(mailSubject);
email.setMsg(mailBody);
......
package com.xxl.job.admin.core.util;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.EncodedResource;
import org.springframework.core.io.support.PropertiesLoaderUtils;
import java.io.IOException;
import java.util.Properties;
/**
* properties util
*
* @author xuxueli 2015-8-28 10:35:53
*/
public class PropertiesUtil {
private static Logger logger = LoggerFactory.getLogger(PropertiesUtil.class);
private static final String file_name = "xxl-job-admin.properties";
public static String getString(String key) {
Properties prop = null;
try {
Resource resource = new ClassPathResource(file_name);
EncodedResource encodedResource = new EncodedResource(resource,"UTF-8");
prop = PropertiesLoaderUtils.loadProperties(encodedResource);
} catch (IOException e) {
logger.error(e.getMessage(), e);
}
if (prop!=null) {
return prop.getProperty(key);
}
return null;
}
}
......@@ -12,8 +12,16 @@ import java.util.List;
*/
public interface XxlJobInfoDao {
public List<XxlJobInfo> pageList(@Param("offset") int offset, @Param("pagesize") int pagesize, @Param("jobGroup") int jobGroup, @Param("executorHandler") String executorHandler);
public int pageListCount(@Param("offset") int offset, @Param("pagesize") int pagesize, @Param("jobGroup") int jobGroup, @Param("executorHandler") String executorHandler);
public List<XxlJobInfo> pageList(@Param("offset") int offset,
@Param("pagesize") int pagesize,
@Param("jobGroup") int jobGroup,
@Param("jobDesc") String jobDesc,
@Param("executorHandler") String executorHandler);
public int pageListCount(@Param("offset") int offset,
@Param("pagesize") int pagesize,
@Param("jobGroup") int jobGroup,
@Param("jobDesc") String jobDesc,
@Param("executorHandler") String executorHandler);
public int save(XxlJobInfo info);
......
......@@ -41,8 +41,7 @@ public interface XxlJobLogDao {
public int triggerCountByHandleCode(@Param("handleCode") int handleCode);
public List<Map<String, Object>> triggerCountByDay(@Param("from") Date from,
@Param("to") Date to,
@Param("handleCode") int handleCode);
@Param("to") Date to);
public int clearLog(@Param("jobGroup") int jobGroup,
@Param("jobId") int jobId,
......
......@@ -13,23 +13,82 @@ import java.util.Map;
* @author xuxueli 2016-5-28 15:30:33
*/
public interface XxlJobService {
public Map<String, Object> pageList(int start, int length, int jobGroup, String executorHandler, String filterTime);
/**
* page list
*
* @param start
* @param length
* @param jobGroup
* @param jobDesc
* @param executorHandler
* @param filterTime
* @return
*/
public Map<String, Object> pageList(int start, int length, int jobGroup, String jobDesc, String executorHandler, String filterTime);
/**
* add job
*
* @param jobInfo
* @return
*/
public ReturnT<String> add(XxlJobInfo jobInfo);
public ReturnT<String> reschedule(XxlJobInfo jobInfo);
/**
* update job
*
* @param jobInfo
* @return
*/
public ReturnT<String> update(XxlJobInfo jobInfo);
/**
* remove job
*
* @param id
* @return
*/
public ReturnT<String> remove(int id);
/**
* pause job
*
* @param id
* @return
*/
public ReturnT<String> pause(int id);
/**
* resume job
*
* @param id
* @return
*/
public ReturnT<String> resume(int id);
/**
* trigger job
*
* @param id
* @return
*/
public ReturnT<String> triggerJob(int id);
/**
* dashboard info
*
* @return
*/
public Map<String,Object> dashboardInfo();
public ReturnT<Map<String,Object>> triggerChartDate(Date startDate, Date endDate);
/**
* chart info
*
* @param startDate
* @param endDate
* @return
*/
public ReturnT<Map<String,Object>> chartInfo(Date startDate, Date endDate);
}
......@@ -2,6 +2,7 @@ package com.xxl.job.admin.service.impl;
import com.xxl.job.admin.core.model.XxlJobInfo;
import com.xxl.job.admin.core.model.XxlJobLog;
import com.xxl.job.admin.core.util.I18nUtil;
import com.xxl.job.admin.dao.XxlJobInfoDao;
import com.xxl.job.admin.dao.XxlJobLogDao;
import com.xxl.job.admin.dao.XxlJobRegistryDao;
......@@ -64,7 +65,7 @@ public class AdminBizImpl implements AdminBiz {
if (IJobHandler.SUCCESS.getCode() == handleCallbackParam.getExecuteResult().getCode()) {
XxlJobInfo xxlJobInfo = xxlJobInfoDao.loadById(log.getJobId());
if (xxlJobInfo!=null && StringUtils.isNotBlank(xxlJobInfo.getChildJobId())) {
callbackMsg = "<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发子任务<<<<<<<<<<< </span><br>";
callbackMsg = "<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>"+ I18nUtil.getString("jobconf_trigger_child_run") +"<<<<<<<<<<< </span><br>";
String[] childJobIds = xxlJobInfo.getChildJobId().split(",");
for (int i = 0; i < childJobIds.length; i++) {
......@@ -73,21 +74,27 @@ public class AdminBizImpl implements AdminBiz {
ReturnT<String> triggerChildResult = xxlJobService.triggerJob(childJobId);
// add msg
callbackMsg += MessageFormat.format("{0}/{1} [任务ID={2}], 触发{3}, 触发备注: {4} <br>",
(i+1), childJobIds.length, childJobIds[i], (triggerChildResult.getCode()==ReturnT.SUCCESS_CODE?"成功":"失败"), triggerChildResult.getMsg());
callbackMsg += MessageFormat.format(I18nUtil.getString("jobconf_callback_child_msg1"),
(i+1),
childJobIds.length,
childJobIds[i],
(triggerChildResult.getCode()==ReturnT.SUCCESS_CODE?I18nUtil.getString("system_success"):I18nUtil.getString("system_fail")),
triggerChildResult.getMsg());
} else {
callbackMsg += MessageFormat.format(" {0}/{1} [任务ID={2}], 触发失败, 触发备注: 任务ID格式错误 <br>",
(i+1), childJobIds.length, childJobIds[i]);
callbackMsg += MessageFormat.format(I18nUtil.getString("jobconf_callback_child_msg2"),
(i+1),
childJobIds.length,
childJobIds[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 = "<br><br><span style=\"color:#F39C12;\" > >>>>>>>>>>>"+ I18nUtil.getString("jobconf_exe_fail_retry") +"<<<<<<<<<<< </span><br>";
callbackMsg += MessageFormat.format("触发{0}, 触发备注: {1}",
(retryTriggerResult.getCode()==ReturnT.SUCCESS_CODE?"成功":"失败"), retryTriggerResult.getMsg());
callbackMsg += MessageFormat.format(I18nUtil.getString("jobconf_callback_msg1"),
(retryTriggerResult.getCode()==ReturnT.SUCCESS_CODE?I18nUtil.getString("system_success"):I18nUtil.getString("system_fail")), retryTriggerResult.getMsg());
}
// handle msg
......
admin_name=任务调度中心
admin_name_full=分布式任务调度平台XXL-JOB
admin_version=1.9.2 (快照版)
## system
system_tips=系统提示
system_ok=确定
system_close=关闭
system_save=保存
system_cancel=取消
system_search=搜索
system_status=状态
system_opt=操作
system_please_input=请输入
system_please_choose=请选择
system_success=成功
system_fail=失败
system_add_suc=新增成功
system_add_fail=新增失败
system_update_suc=更新成功
system_update_fail=更新失败
system_all=全部
system_api_error=接口异常
system_show=查看
system_empty=
system_opt_suc=操作成功
system_opt_fail=操作失败
system_opt_edit=编辑
system_opt_del=删除
system_unvalid=非法
system_not_found=不存在
system_nav=导航
## daterangepicker
daterangepicker_ranges_recent_hour=最近一小时
daterangepicker_ranges_today=今日
daterangepicker_ranges_yesterday=昨日
daterangepicker_ranges_this_month=本月
daterangepicker_ranges_last_month=上个月
daterangepicker_ranges_recent_week=最近一周
daterangepicker_ranges_recent_month=最近一月
daterangepicker_custom_name=自定义
daterangepicker_custom_starttime=起始时间
daterangepicker_custom_endtime=结束时间
daterangepicker_custom_daysofweek=日,一,二,三,四,五,六
daterangepicker_custom_monthnames=一月,二月,三月,四月,五月,六月,七月,八月,九月,十月,十一月,十二月
## dataTable
dataTable_sProcessing=处理中...
dataTable_sLengthMenu=每页 _MENU_ 条记录
dataTable_sZeroRecords=没有匹配结果
dataTable_sInfo=第 _PAGE_ 页 ( 总共 _PAGES_ 页,_TOTAL_ 条记录 )
dataTable_sInfoEmpty=无记录
dataTable_sInfoFiltered=(由 _MAX_ 项结果过滤)
dataTable_sSearch=搜索
dataTable_sEmptyTable=表中数据为空
dataTable_sLoadingRecords=载入中...
dataTable_sFirst=首页
dataTable_sPrevious=上页
dataTable_sNext=下页
dataTable_sLast=末页
dataTable_sSortAscending=: 以升序排列此列
dataTable_sSortDescending=: 以降序排列此列
## login
login_btn=登录
login_remember_me=记住密码
login_username_placeholder=请输入登录账号
login_password_placeholder=请输入登录密码
login_username_empty=请输入登录账号
login_username_lt_5=登录账号不应低于5位
login_password_empty=请输入登录密码
login_password_lt_5=登录密码不应低于5位
login_success=登录成功
login_fail=登录失败
login_param_empty=账号或密码为空
login_param_unvalid=账号或密码错误
## logout
logout_btn=注销
logout_confirm=确认注销登录?
logout_success=注销成功
logout_fail=注销失败
## dashboard
job_dashboard_name=运行报表
job_dashboard_job_num=任务数量
job_dashboard_job_num_tip=调度中心运行的任务数量
job_dashboard_trigger_num=调度次数
job_dashboard_trigger_num_tip=调度中心触发的调度次数
job_dashboard_jobgroup_num=执行器数量
job_dashboard_jobgroup_num_tip=调度中心在线的执行器机器数量
job_dashboard_report=调度报表
job_dashboard_report_loaddata_fail=调度报表数据加载异常
job_dashboard_date_report=日期分布图
job_dashboard_rate_report=成功比例图
## job info
jobinfo_name=任务管理
jobinfo_job=任务
jobinfo_field_add=新增任务
jobinfo_field_update=更新任务
jobinfo_field_id=任务ID
jobinfo_field_jobgroup=执行器
jobinfo_field_jobdesc=任务描述
jobinfo_field_gluetype=运行模式
jobinfo_field_executorparam=任务参数
jobinfo_field_cron_unvalid=Cron格式非法
jobinfo_field_author=负责人
jobinfo_field_alarmemail=报警邮件
jobinfo_field_alarmemail_placeholder=请输入报警邮件,多个邮件地址则逗号分隔
jobinfo_field_executorRouteStrategy=路由策略
jobinfo_field_childJobId=子任务ID
jobinfo_field_childJobId_limit=子任务ID({0})不可与父任务重复
jobinfo_field_childJobId_placeholder=请输入子任务的任务ID,如存在多个则逗号分隔
jobinfo_field_executorBlockStrategy=阻塞处理策略
jobinfo_field_executorFailStrategy=失败处理策略
jobinfo_script_location=脚本位置
jobinfo_shard_index=分片序号
jobinfo_shard_total=分片总数
jobinfo_opt_pause=暂停
jobinfo_opt_resume=恢复
jobinfo_opt_log=日志
jobinfo_opt_run=执行
jobinfo_glue_remark=源码备注
jobinfo_glue_remark_limit=源码备注长度限制为4~100
jobinfo_glue_rollback=版本回溯
jobinfo_glue_jobid_unvalid=任务ID非法
jobinfo_glue_gluetype_unvalid=该任务非GLUE模式
## job log
joblog_name=调度日志
joblog_status=状态
joblog_status_all=全部
joblog_status_suc=成功
joblog_status_fail=失败
joblog_status_running=进行中
joblog_field_triggerTime=调度时间
joblog_field_triggerCode=调度结果
joblog_field_triggerMsg=调度备注
joblog_field_handleTime=执行时间
joblog_field_handleCode=执行结果
joblog_field_handleMsg=执行备注
joblog_field_executorAddress=执行器地址
joblog_clean=清理
joblog_clean_log=日志清理
joblog_clean_type=清理方式
joblog_clean_type_1=清理一个月之前日志数据
joblog_clean_type_2=清理三个月之前日志数据
joblog_clean_type_3=清理六个月之前日志数据
joblog_clean_type_4=清理一年之前日志数据
joblog_clean_type_5=清理一千条以前日志数据
joblog_clean_type_6=清理一万条以前日志数据
joblog_clean_type_7=清理三万条以前日志数据
joblog_clean_type_8=清理十万条以前日志数据
joblog_clean_type_9=清理所有日志数据
joblog_clean_type_unvalid=清理类型参数异常
joblog_handleCode_200=成功
joblog_handleCode_500=失败
joblog_handleCode_501=失败重试
joblog_kill_log=终止任务
joblog_kill_log_limit=调度失败,无法终止日志
joblog_kill_log_byman=人为操作主动终止
joblog_rolling_log=执行日志
joblog_rolling_log_refresh=刷新
joblog_rolling_log_triggerfail=任务发起调度失败,无法查看执行日志
joblog_rolling_log_failoften=终止请求Rolling日志,请求失败次数超上限,可刷新页面重新加载日志
joblog_logid_unvalid=日志ID非法
## job group
jobgroup_name=执行器管理
jobgroup_list=执行器列表
jobgroup_add=新增执行器
jobgroup_edit=编辑执行器
jobgroup_del=删除执行器
jobgroup_field_order=排序
jobgroup_field_title=名称
jobgroup_field_addressType=注册方式
jobgroup_field_addressType_0=自动注册
jobgroup_field_addressType_1=手动录入
jobgroup_field_addressType_limit=手动录入注册方式,机器地址不可为空
jobgroup_field_registryList=机器地址
jobgroup_field_registryList_unvalid=机器地址格式非法
jobgroup_field_registryList_placeholder=请输入执行器地址列表,多地址逗号分隔
jobgroup_field_appName_limit=限制以小写字母开头,由小写字母、数字和中划线组成
jobgroup_field_appName_length=AppName长度限制为4~64
jobgroup_field_title_length=名称长度限制为4~12
jobgroup_field_order_digits=请输入整数
jobgroup_field_orderrange=取值范围为1~1000
jobgroup_del_limit_0=拒绝删除,该执行器使用中
jobgroup_del_limit_1=拒绝删除, 系统至少保留一个执行器
## job conf
jobconf_fail_alarm=失败告警
jobconf_fail_retry=失败重试
jobconf_route_first=第一个
jobconf_route_last=最后一个
jobconf_route_round=轮询
jobconf_route_random=随机
jobconf_route_consistenthash=一致性HASH
jobconf_route_lfu=最不经常使用
jobconf_route_lru=最近最久未使用
jobconf_route_failover=故障转移
jobconf_route_busyover=忙碌转移
jobconf_route_shard=分片广播
jobconf_idleBeat=空闲检测
jobconf_beat=心跳检测
jobconf_monitor=调度中心监控报警
jobconf_monitor_detail=监控告警明细
jobconf_monitor_alarm_title=告警类型
jobconf_monitor_alarm_type=调度失败
jobconf_trigger_admin_adress=调度机器
jobconf_trigger_exe_regtype=执行器-注册方式
jobconf_trigger_exe_regaddress=执行器-地址列表
jobconf_trigger_address_empty=调度失败:执行器地址为空
jobconf_trigger_run=触发调度
jobconf_trigger_child_run=触发子任务
jobconf_trigger_fail_retry=调度失败重试
jobconf_exe_fail_retry=执行失败重试
jobconf_callback_child_msg1={0}/{1} [任务ID={2}], 触发{3}, 触发备注: {4} <br>
jobconf_callback_child_msg2={0}/{1} [任务ID={2}], 触发失败, 触发备注: 任务ID格式错误 <br>
jobconf_callback_msg1=触发{0}, 触发备注: {1} <br>
## help
job_help=使用教程
job_help_document=官方文档
\ No newline at end of file
admin_name=Scheduling Center
admin_name_full=Distributed Task Scheduling Platform XXL-JOB
admin_version=1.9.2 (SNAPSHOT)
## system
system_tips=System message
system_ok=Confirm
system_close=Close
system_save=Save
system_cancel=Cancel
system_search=Search
system_status=Status
system_opt=Operate
system_please_input=please input
system_please_choose=please choose
system_success=success
system_fail=fail
system_add_suc=add success
system_add_fail=add fail
system_update_suc=update success
system_update_fail=update fail
system_all=All
system_api_error=net error
system_show=Show
system_empty=Empty
system_opt_suc=operate success
system_opt_fail=operate fail
system_opt_edit=Edit
system_opt_del=Delete
system_unvalid=illegal
system_not_found=not exist
system_nav=Navigation
## daterangepicker
daterangepicker_ranges_recent_hour=recent one hour
daterangepicker_ranges_today=today
daterangepicker_ranges_yesterday=yesterday
daterangepicker_ranges_this_month=this month
daterangepicker_ranges_last_month=last month
daterangepicker_ranges_recent_week=recent one week
daterangepicker_ranges_recent_month=recent one month
daterangepicker_custom_name=custom
daterangepicker_custom_starttime=start time
daterangepicker_custom_endtime=end time
daterangepicker_custom_daysofweek=Sun,Mon,Tue,Wed,Thu,Fri,Sat
daterangepicker_custom_monthnames=Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec
## dataTable
dataTable_sProcessing=processing...
dataTable_sLengthMenu= _MENU_ records per page
dataTable_sZeroRecords=No matching results
dataTable_sInfo=page _PAGE_ ( Total _PAGES_ pages,_TOTAL_ records )
dataTable_sInfoEmpty=No Record
dataTable_sInfoFiltered=(Filtered by _MAX_ results)
dataTable_sSearch=Search
dataTable_sEmptyTable=Table data is empty
dataTable_sLoadingRecords=Loading...
dataTable_sFirst=FIRST PAGE
dataTable_sPrevious=Previous Page
dataTable_sNext=Next Page
dataTable_sLast=LAST PAGE
dataTable_sSortAscending=: Rank this column in ascending order
dataTable_sSortDescending=: Rank this column in descending order
## login
login_btn=Login
login_remember_me=Remember Me
login_username_placeholder=Please enter username
login_password_placeholder=Please enter password
login_username_empty=Please enter username
login_username_lt_5=Username length should not be less than 5
login_password_empty=Please enter password
login_password_lt_5=Password length should not be less than 5
login_success=Login success
login_fail=Login fail
login_param_empty=Username or password is empty
login_param_unvalid=Username or password error
## logout
logout_btn=Logout
logout_confirm=Confirm logout?
logout_success=Logout success
logout_fail=Logout fail
## dashboard
job_dashboard_name=Run report
job_dashboard_job_num=Job number
job_dashboard_job_num_tip=The number of tasks running in the scheduling center
job_dashboard_trigger_num=trigger number
job_dashboard_trigger_num_tip=The number of trigger record scheduled by the scheduling center
job_dashboard_jobgroup_num=Executor number
job_dashboard_jobgroup_num_tip=The number of online executor machines perceived by the scheduling center
job_dashboard_report=Scheduling report
job_dashboard_report_loaddata_fail=Scheduling report load data error
job_dashboard_date_report=Date distribution
job_dashboard_rate_report=Percentage distribution
## job info
jobinfo_name=Job Manage
jobinfo_job=Job
jobinfo_field_add=Add Job
jobinfo_field_update=Edit Job
jobinfo_field_id=Job ID
jobinfo_field_jobgroup=Executor
jobinfo_field_jobdesc=Job description
jobinfo_field_gluetype=GLUE Type
jobinfo_field_executorparam=Param
jobinfo_field_cron_unvalid=The Cron is illegal
jobinfo_field_author=Author
jobinfo_field_alarmemail=Alarm email
jobinfo_field_alarmemail_placeholder=Please enter alarm mail, if there are more than one comma separated
jobinfo_field_executorRouteStrategy=Route Strategy
jobinfo_field_childJobId=Child Job ID
jobinfo_field_childJobId_limit=Child job ID({0}) cannot be duplicated with the parent job.
jobinfo_field_childJobId_placeholder=Please enter the Child job ID, if there are more than one comma separated
jobinfo_field_executorBlockStrategy=Block Strategy
jobinfo_field_executorFailStrategy=Fail Strategy
jobinfo_script_location=Script location
jobinfo_shard_index=Shard index
jobinfo_shard_total=Shard total
jobinfo_opt_pause=Pause
jobinfo_opt_resume=Resume
jobinfo_opt_log=Log
jobinfo_opt_run=Run
jobinfo_glue_remark=Resource Remark
jobinfo_glue_remark_limit=Resource Remark length is limited to 4~100
jobinfo_glue_rollback=Version Backtrack
jobinfo_glue_jobid_unvalid=Job ID is illegal
jobinfo_glue_gluetype_unvalid=The job is not GLUE Type
## job log
joblog_name=Trigger Log
joblog_status=Status
joblog_status_all=All
joblog_status_suc=Success
joblog_status_fail=Fail
joblog_status_running=Running
joblog_field_triggerTime=Trigger Time
joblog_field_triggerCode=Trigger Result
joblog_field_triggerMsg=Trigger Msg
joblog_field_handleTime=Handle Time
joblog_field_handleCode=Handle Result
joblog_field_handleMsg=Trigger Msg
joblog_field_executorAddress=Executor Address
joblog_clean=Clean
joblog_clean_log=Clean Log
joblog_clean_type=Clean Type
joblog_clean_type_1=Clean up log data a month ago
joblog_clean_type_2=Clean up log data three month ago
joblog_clean_type_3=Clean up log data six month ago
joblog_clean_type_4=Clean up log data a year ago
joblog_clean_type_5=Clean up log data a thousand record ago
joblog_clean_type_6=Clean up log data ten thousand record ago
joblog_clean_type_7=Clean up log data thirty thousand record ago
joblog_clean_type_8=Clean up log data hundred thousand record ago
joblog_clean_type_9=Clean up all log data
joblog_clean_type_unvalid=Clean type is illegal
joblog_handleCode_200=Success
joblog_handleCode_500=Fail
joblog_handleCode_501=Fail retry
joblog_kill_log=Kill Job
joblog_kill_log_limit=Trigger Fail, can not kill job
joblog_kill_log_byman=Manual operation to active kill job
joblog_rolling_log=Rolling log
joblog_rolling_log_refresh=Refresh
joblog_rolling_log_triggerfail=The job trigger fail, can not view the rolling log
joblog_rolling_log_failoften=The request for the Rolling log is terminated, the number of failed requests exceeds the limit, Reload the log on the refresh page
joblog_logid_unvalid=Log ID is illegal
## job group
jobgroup_name=Executor Manage
jobgroup_list=Executor List
jobgroup_add=Add Executor
jobgroup_edit=Edit Executor
jobgroup_del=Delete Executor
jobgroup_field_order=Order
jobgroup_field_title=Title
jobgroup_field_addressType=Registry Type
jobgroup_field_addressType_0=Automatic registration
jobgroup_field_addressType_1=Manual registration
jobgroup_field_addressType_limit=Manually registration type, the machine address must not be empty
jobgroup_field_registryList=machine address
jobgroup_field_registryList_unvalid=registry machine address is illegal
jobgroup_field_registryList_placeholder=Please enter the machine address, if there are more than one comma separated
jobgroup_field_appName_limit=Limit the beginning of a lowercase letter, consists of lowercase letters、number and underscores.
jobgroup_field_appName_length=AppName length is limited to 4~64
jobgroup_field_title_length=Title length is limited to 4~12
jobgroup_field_order_digits=Please enter a positive integer
jobgroup_field_orderrange=Order is limited to 1~1000
jobgroup_del_limit_0=Refuse to delete, the executor is being used
jobgroup_del_limit_1=Refuses to delete, the system retains at least one executor
## job conf
jobconf_fail_alarm=Fail Alarm
jobconf_fail_retry=Fail Retry
jobconf_route_first=First
jobconf_route_last=Last
jobconf_route_round=Round
jobconf_route_random=Random
jobconf_route_consistenthash=Consistent Hash
jobconf_route_lfu=Least Frequently Used
jobconf_route_lru=Least Recently Used
jobconf_route_failover=Failover
jobconf_route_busyover=Busyover
jobconf_route_shard=Sharding Broadcast
jobconf_idleBeat=Idle check
jobconf_beat=Heartbeats
jobconf_monitor=Scheduling Center monitor alarm
jobconf_monitor_detail=monitor alarm details
jobconf_monitor_alarm_title=Alarm Type
jobconf_monitor_alarm_type=Trigger Fail
jobconf_trigger_admin_adress=Trigger machine address
jobconf_trigger_exe_regtype=Execotor-Registry Type
jobconf_trigger_exe_regaddress=Execotor-Registry Address
jobconf_trigger_address_empty=Trigger Fail:registry address is empty
jobconf_trigger_run=Trigger Job
jobconf_trigger_child_run=Trigger child job
jobconf_trigger_fail_retry=Trigger fail retry
jobconf_exe_fail_retry=Handle fail retry
jobconf_callback_child_msg1={0}/{1} [Job ID={2}], Trigger {3}, Trigger msg: {4} <br>
jobconf_callback_child_msg2={0}/{1} [Job ID={2}], Trigger Fail, Trigger msg: Job ID is illegal <br>
jobconf_callback_msg1=Trigger {0}, Trigger msg: {1} <br>
## help
job_help=Tutorial
job_help_document=Official Document
\ No newline at end of file
......@@ -58,6 +58,9 @@
<if test="jobGroup gt 0">
AND t.job_group = #{jobGroup}
</if>
<if test="jobDesc != null and jobDesc != ''">
AND t.job_desc like CONCAT(CONCAT('%', #{jobDesc}), '%')
</if>
<if test="executorHandler != null and executorHandler != ''">
AND t.executor_handler like CONCAT(CONCAT('%', #{executorHandler}), '%')
</if>
......@@ -73,6 +76,9 @@
<if test="jobGroup gt 0">
AND t.job_group = #{jobGroup}
</if>
<if test="jobDesc != null and jobDesc != ''">
AND t.job_desc like CONCAT(CONCAT('%', #{jobDesc}), '%')
</if>
<if test="executorHandler != null and executorHandler != ''">
AND t.executor_handler like CONCAT(CONCAT('%', #{executorHandler}), '%')
</if>
......
......@@ -163,13 +163,14 @@
</select>
<select id="triggerCountByDay" resultType="java.util.Map" >
SELECT DATE_FORMAT(trigger_time,'%Y-%m-%d') triggerDay, COUNT(id) triggerCount
FROM XXL_JOB_QRTZ_TRIGGER_LOG
WHERE trigger_time BETWEEN #{from} and #{to}
<if test="handleCode gt 0">
AND handle_code = #{handleCode}
</if>
GROUP BY triggerDay;
SELECT
DATE_FORMAT(trigger_time,'%Y-%m-%d') triggerDay,
COUNT(handle_code) triggerDayCount,
SUM(CASE WHEN handle_code = 0 then 1 else 0 end) as triggerDayCountRunning,
SUM(CASE WHEN handle_code = 200 then 1 else 0 end) as triggerDayCountSuc
FROM XXL_JOB_QRTZ_TRIGGER_LOG
WHERE trigger_time BETWEEN #{from} and #{to}
GROUP BY triggerDay;
</select>
<delete id="clearLog" >
......
......@@ -20,7 +20,7 @@ org.quartz.jobStore.maxMisfiresToHandleAtATime: 1
#org.quartz.jobStore.class: org.quartz.simpl.RAMJobStore
# for cluster
org.quartz.jobStore.tablePrefix = XXL_JOB_QRTZ_
org.quartz.jobStore.tablePrefix: XXL_JOB_QRTZ_
org.quartz.jobStore.class: org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.isClustered: true
org.quartz.jobStore.clusterCheckinInterval: 5000
......@@ -7,7 +7,7 @@
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.xxl.job.admin.service, com.xxl.job.admin.dao" />
<context:component-scan base-package="com.xxl.job.admin.core.conf, com.xxl.job.admin.service, com.xxl.job.admin.dao" />
<bean id="freemarkerConfig" class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
<property name="templateLoaderPath" value="/WEB-INF/template/" />
......
......@@ -16,4 +16,7 @@ xxl.job.login.username=admin
xxl.job.login.password=123456
### xxl-job, access token
xxl.job.accessToken=
\ No newline at end of file
xxl.job.accessToken=
### xxl-job, i18n (default empty as chinese, "en" as english)
xxl.job.i18n=
\ No newline at end of file
......@@ -2,7 +2,7 @@
<html>
<head>
<meta charset="UTF-8">
<title>应用程序异常 (500)</title>
<title>Error</title>
<style type="text/css">
body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }
div.dialog {
......@@ -21,8 +21,9 @@
<body>
<div class="dialog">
<h1>应用程序异常</h1>
<p>抱歉!您访问的页面出现异常,请稍后重试或联系管理员。</p>
<h1>System Error</h1>
<p>Oops! Page not found.</p>
<a href="javascript:window.location.href='${request.contextPath}/'">Back</a>
</div>
</body>
......
......@@ -2,7 +2,7 @@
<html>
<head>
<meta charset="UTF-8">
<title>应用程序异常 (error)</title>
<title>Error</title>
<style type="text/css">
body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }
div.dialog {
......@@ -21,9 +21,9 @@
<body>
<div class="dialog">
<h1>应用程序异常</h1>
<h1>System Error</h1>
<p>${exceptionMsg}</p>
<a href="javascript:window.location.href='${request.contextPath}/'">返 回</a>
<a href="javascript:window.location.href='${request.contextPath}/'">Back</a>
</p>
</div>
......
......@@ -29,6 +29,10 @@
<!-- pace -->
<link rel="stylesheet" href="${request.contextPath}/static/plugins/pace/themes/pace-theme-flash.css">
<#-- i18n -->
<#global I18n = I18nUtil.getMultString()?eval />
</#macro>
<#macro commonScript>
......@@ -53,7 +57,10 @@
<#-- common -->
<script src="${request.contextPath}/static/js/common.1.js"></script>
<script>var base_url = '${request.contextPath}';</script>
<script>
var base_url = '${request.contextPath}';
var I18n = ${I18nUtil.getMultString()};
</script>
</#macro>
......@@ -61,7 +68,7 @@
<header class="main-header">
<a href="${request.contextPath}/" class="logo">
<span class="logo-mini"><b>XXL</b></span>
<span class="logo-lg"><b>任务调度中心</b></span>
<span class="logo-lg"><b>${I18n.admin_name}</b></span>
</a>
<nav class="navbar navbar-static-top" role="navigation">
<a href="#" class="sidebar-toggle" data-toggle="offcanvas" role="button"><span class="sr-only">切换导航</span></a>
......@@ -69,7 +76,7 @@
<ul class="nav navbar-nav">
<li class="dropdown user user-menu">
<a href=";" id="logoutBtn" class="dropdown-toggle" data-toggle="dropdown" aria-expanded="false">
<span class="hidden-xs">注销</span>
<span class="hidden-xs">${I18n.logout_btn}</span>
</a>
</li>
</ul>
......@@ -85,11 +92,11 @@
<section class="sidebar">
<!-- sidebar menu: : style can be found in sidebar.less -->
<ul class="sidebar-menu">
<li class="header">常用模块</li>
<li class="nav-click <#if pageName == "jobinfo">active</#if>" ><a href="${request.contextPath}/jobinfo"><i class="fa fa-circle-o text-aqua"></i><span>任务管理</span></a></li>
<li class="nav-click <#if pageName == "joblog">active</#if>" ><a href="${request.contextPath}/joblog"><i class="fa fa-circle-o text-yellow"></i><span>调度日志</span></a></li>
<li class="nav-click <#if pageName == "jobgroup">active</#if>" ><a href="${request.contextPath}/jobgroup"><i class="fa fa-circle-o text-green"></i><span>执行器管理</span></a></li>
<li class="nav-click <#if pageName == "help">active</#if>" ><a href="${request.contextPath}/help"><i class="fa fa-circle-o text-gray"></i><span>使用教程</span></a></li>
<li class="header">${I18n.system_nav}</li>
<li class="nav-click <#if pageName == "jobinfo">active</#if>" ><a href="${request.contextPath}/jobinfo"><i class="fa fa-circle-o text-aqua"></i><span>${I18n.jobinfo_name}</span></a></li>
<li class="nav-click <#if pageName == "joblog">active</#if>" ><a href="${request.contextPath}/joblog"><i class="fa fa-circle-o text-yellow"></i><span>${I18n.joblog_name}</span></a></li>
<li class="nav-click <#if pageName == "jobgroup">active</#if>" ><a href="${request.contextPath}/jobgroup"><i class="fa fa-circle-o text-green"></i><span>${I18n.jobgroup_name}</span></a></li>
<li class="nav-click <#if pageName == "help">active</#if>" ><a href="${request.contextPath}/help"><i class="fa fa-circle-o text-gray"></i><span>${I18n.job_help}</span></a></li>
</ul>
</section>
<!-- /.sidebar -->
......@@ -175,7 +182,7 @@
<#macro commonFooter >
<footer class="main-footer">
Powered by <b>XXL-JOB</b> 1.9.0(快照版本)
Powered by <b>XXL-JOB</b> ${I18n.admin_version}
<div class="pull-right hidden-xs">
<strong>Copyright &copy; 2015-${.now?string('yyyy')} &nbsp;
<a href="http://www.xuxueli.com/" target="_blank" >xuxueli</a>
......
<!DOCTYPE html>
<html>
<head>
<title>任务调度中心</title>
<#import "/common/common.macro.ftl" as netCommon>
<@netCommon.commonStyle />
<title>${I18n.admin_name}</title>
</head>
<body class="hold-transition skin-blue sidebar-mini <#if cookieMap?exists && "off" == cookieMap["xxljob_adminlte_settings"].value >sidebar-collapse</#if> ">
<div class="wrapper">
......@@ -16,25 +16,19 @@
<div class="content-wrapper">
<!-- Content Header (Page header) -->
<section class="content-header">
<h1>使用教程<small>任务调度中心</small></h1>
<!--
<ol class="breadcrumb">
<li><a><i class="fa fa-dashboard"></i>调度中心</a></li>
<li class="active">使用教程</li>
</ol>
-->
<h1>${I18n.job_help}</h1>
</section>
<!-- Main content -->
<section class="content">
<div class="callout callout-info">
<h4>分布式任务调度平台XXL-JOB</h4>
<h4>${I18n.admin_name_full}</h4>
<br>
<p>
<a target="_blank" href="https://github.com/xuxueli/xxl-job">github</a>&nbsp;&nbsp;&nbsp;&nbsp;
<a target="_blank" href="https://github.com/xuxueli/xxl-job">Github</a>&nbsp;&nbsp;&nbsp;&nbsp;
<iframe src="https://ghbtns.com/github-btn.html?user=xuxueli&repo=xxl-job&type=star&count=true" frameborder="0" scrolling="0" width="170px" height="20px" style="margin-bottom:-5px;"></iframe>
<br><br>
<a target="_blank" href="http://www.xuxueli.com/xxl-job/">文档地址</a>
<a target="_blank" href="http://www.xuxueli.com/xxl-job/">${I18n.job_help_document}</a>
<br><br>
</p>
......
<!DOCTYPE html>
<html>
<head>
<title>任务调度中心</title>
<#import "/common/common.macro.ftl" as netCommon>
<@netCommon.commonStyle />
<!-- daterangepicker -->
<link rel="stylesheet" href="${request.contextPath}/static/adminlte/plugins/daterangepicker/daterangepicker.css">
<title>${I18n.admin_name}</title>
</head>
<body class="hold-transition skin-blue sidebar-mini <#if cookieMap?exists && "off" == cookieMap["xxljob_adminlte_settings"].value >sidebar-collapse</#if> ">
<div class="wrapper">
......@@ -18,8 +18,9 @@
<div class="content-wrapper">
<!-- Content Header (Page header) -->
<section class="content-header">
<h1>运行报表<small>任务调度中心</small></h1>
<h1>${I18n.job_dashboard_name}</h1>
<!--
<h1>运行报表<small>任务调度中心</small></h1>
<ol class="breadcrumb">
<li><a><i class="fa fa-dashboard"></i>调度中心</a></li>
<li class="active">使用教程</li>
......@@ -39,13 +40,13 @@
<span class="info-box-icon"><i class="fa fa-flag-o"></i></span>
<div class="info-box-content">
<span class="info-box-text">任务数量</span>
<span class="info-box-text">${I18n.job_dashboard_job_num}</span>
<span class="info-box-number">${jobInfoCount}</span>
<div class="progress">
<div class="progress-bar" style="width: 100%"></div>
</div>
<span class="progress-description">调度中心运行的任务数量</span>
<span class="progress-description">${I18n.job_dashboard_job_num_tip}</span>
</div>
</div>
</div>
......@@ -56,14 +57,14 @@
<span class="info-box-icon"><i class="fa fa-calendar"></i></span>
<div class="info-box-content">
<span class="info-box-text">调度次数</span>
<span class="info-box-text">${I18n.job_dashboard_trigger_num}</span>
<span class="info-box-number">${jobLogCount}</span>
<div class="progress">
<div class="progress-bar" style="width: 100%" ></div>
</div>
<span class="progress-description">
调度中心触发的调度次数
${I18n.job_dashboard_trigger_num_tip}
<#--<#if jobLogCount gt 0>
调度成功率:${(jobLogSuccessCount*100/jobLogCount)?string("0.00")}<small>%</small>
</#if>-->
......@@ -78,13 +79,13 @@
<span class="info-box-icon"><i class="fa ion-ios-settings-strong"></i></span>
<div class="info-box-content">
<span class="info-box-text">执行器数量</span>
<span class="info-box-text">${I18n.job_dashboard_jobgroup_num}</span>
<span class="info-box-number">${executorCount}</span>
<div class="progress">
<div class="progress-bar" style="width: 100%"></div>
</div>
<span class="progress-description">调度中心在线的执行器机器数量</span>
<span class="progress-description">${I18n.job_dashboard_jobgroup_num_tip}</span>
</div>
</div>
</div>
......@@ -96,7 +97,7 @@
<div class="col-md-12">
<div class="box">
<div class="box-header with-border">
<h3 class="box-title">调度报表</h3>
<h3 class="box-title">${I18n.job_dashboard_report}</h3>
<#--<input type="text" class="form-control" id="filterTime" readonly >-->
<!-- tools box -->
......
<!DOCTYPE html>
<html>
<head>
<title>任务调度中心</title>
<#import "/common/common.macro.ftl" as netCommon>
<@netCommon.commonStyle />
<link rel="stylesheet" href="${request.contextPath}/static/plugins/codemirror/lib/codemirror.css">
<link rel="stylesheet" href="${request.contextPath}/static/plugins/codemirror/addon/hint/show-hint.css">
<title>${I18n.admin_name}</title>
<style type="text/css">
.CodeMirror {
font-size:16px;
......@@ -35,7 +35,11 @@
<#-- left nav -->
<div class="collapse navbar-collapse pull-left" id="navbar-collapse">
<ul class="nav navbar-nav">
<li class="active" ><a href="javascript:;"><#list GlueTypeEnum as item><#if item == jobInfo.glueType>${item.desc}</#if></#list> 任务:${jobInfo.jobDesc}<span class="sr-only">(current)</span></a></li>
<li class="active" ><a href="javascript:;">
<span class="sr-only">(current)</span>
<#list GlueTypeEnum as item><#if item == jobInfo.glueType>${item.desc}</#if></#list>
${jobInfo.jobDesc}
</a></li>
</ul>
</div>
......@@ -43,7 +47,7 @@
<div class="navbar-custom-menu">
<ul class="nav navbar-nav">
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" aria-expanded="false">版本回溯 <span class="caret"></span></a>
<a href="#" class="dropdown-toggle" data-toggle="dropdown" aria-expanded="false">${I18n.jobinfo_glue_rollback} <span class="caret"></span></a>
<ul class="dropdown-menu" role="menu">
<li <#if jobLogGlues?exists && jobLogGlues?size gt 0 >style="display: none;"</#if> >
<a href="javascript:;" class="source_version" version="version_now" glueType="${jobInfo.glueType}" >
......@@ -66,7 +70,7 @@
<li id="save" >
<a href="javascript:;" >
<i class="fa fa-fw fa-save" ></i>
保存
${I18n.system_save}
</a>
</li>
</ul>
......@@ -87,19 +91,19 @@
<div class="modal-dialog ">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title" ><i class="fa fa-fw fa-save"></i>保存</h4>
<h4 class="modal-title" ><i class="fa fa-fw fa-save"></i>${I18n.system_save}</h4>
</div>
<div class="modal-body">
<form class="form-horizontal form" role="form" >
<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" id="glueRemark" placeholder="请输入备注信息" maxlength="64" ></div>
<label for="lastname" class="col-sm-2 control-label">${I18n.jobinfo_glue_remark}<font color="red">*</font></label>
<div class="col-sm-10"><input type="text" class="form-control" id="glueRemark" placeholder="${I18n.system_please_input}${I18n.jobinfo_glue_remark}" maxlength="64" ></div>
</div>
<hr>
<div class="form-group">
<div class="col-sm-offset-3 col-sm-6">
<button type="button" class="btn btn-primary ok" >保存</button>
<button type="button" class="btn btn-default" data-dismiss="modal">取消</button>
<button type="button" class="btn btn-primary ok" >${I18n.system_save}</button>
<button type="button" class="btn btn-default" data-dismiss="modal">${I18n.system_cancel}</button>
</div>
</div>
</form>
......
<!DOCTYPE html>
<html>
<head>
<title>任务调度中心</title>
<#import "/common/common.macro.ftl" as netCommon>
<@netCommon.commonStyle />
<#import "/common/common.macro.ftl" as netCommon>
<@netCommon.commonStyle />
<title>${I18n.admin_name}</title>
</head>
<body class="hold-transition skin-blue layout-top-nav">
......@@ -12,9 +12,9 @@
<header class="main-header">
<nav class="navbar navbar-static-top">
<div class="container">
<#-- icon -->
<#-- icon -->
<div class="navbar-header">
<a class="navbar-brand"><b>执行日志</b>Console</a>
<a class="navbar-brand"><b>${I18n.joblog_rolling_log}</b> Console</a>
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar-collapse">
<i class="fa fa-bars"></i>
</button>
......@@ -33,7 +33,7 @@
<li>
<a href="javascript:window.location.reload();" >
<i class="fa fa-fw fa-refresh" ></i>
刷新
${I18n.joblog_rolling_log_refresh}
</a>
</li>
</ul>
......
<!DOCTYPE html>
<html>
<head>
<title>调度中心</title>
<#import "/common/common.macro.ftl" as netCommon>
<@netCommon.commonStyle />
<link rel="stylesheet" href="${request.contextPath}/static/adminlte/plugins/iCheck/square/blue.css">
<title>${I18n.admin_name}</title>
</head>
<body class="hold-transition login-page">
<div class="login-box">
......@@ -13,25 +13,25 @@
</div>
<form id="loginForm" method="post" >
<div class="login-box-body">
<p class="login-box-msg">任务调度中心</p>
<p class="login-box-msg">${I18n.admin_name}</p>
<div class="form-group has-feedback">
<input type="text" name="userName" class="form-control" placeholder="请输入登录账号" value="admin" >
<input type="text" name="userName" class="form-control" placeholder="${I18n.login_username_placeholder}" value="admin" maxlength="18" >
<span class="glyphicon glyphicon-envelope form-control-feedback"></span>
</div>
<div class="form-group has-feedback">
<input type="password" name="password" class="form-control" placeholder="请输入登录密码" value="123456" >
<input type="password" name="password" class="form-control" placeholder="${I18n.login_password_placeholder}" value="123456" maxlength="18" >
<span class="glyphicon glyphicon-lock form-control-feedback"></span>
</div>
<div class="row">
<div class="col-xs-8">
<div class="checkbox icheck">
<label>
<input type="checkbox" name="ifRemember" > Remember Me
<input type="checkbox" name="ifRemember" >${I18n.login_remember_me}
</label>
</div>
</div><!-- /.col -->
<div class="col-xs-4">
<button type="submit" class="btn btn-primary btn-block btn-flat">登录</button>
<button type="submit" class="btn btn-primary btn-block btn-flat">${I18n.login_btn}</button>
</div>
</div>
</div>
......
......@@ -2,27 +2,24 @@ $(function(){
// logout
$("#logoutBtn").click(function(){
layer.confirm('确认注销登录?', {icon: 3, title:'系统提示'}, function(index){
layer.confirm( I18n.logout_confirm , {
icon: 3,
title: I18n.system_tips ,
btn: [ I18n.system_ok, I18n.system_cancel ]
}, function(index){
layer.close(index);
$.post(base_url + "/logout", function(data, status) {
if (data.code == "200") {
layer.msg('注销成功');
layer.msg( I18n.logout_success );
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: '系统提示',
content: (data.msg || "操作失败"),
title: I18n.system_tips ,
btn: [ I18n.system_ok ],
content: (data.msg || I18n.logout_fail),
icon: '2'
});
}
......@@ -68,14 +65,14 @@ $(function(){
}
});
$(slideToTop).click(function () {
$("body").animate({
$("html,body").animate({ // firefox ie not support body, chrome support body. but found that new version chrome not support body too.
scrollTop: 0
}, 100);
});
// 左侧菜单状态,js + 后端 + cookie方式(新)
// left menu status v: js + server + cookie
$('.sidebar-toggle').click(function(){
var xxljob_adminlte_settings = $.cookie('xxljob_adminlte_settings'); // 左侧菜单展开状态[xxljob_adminlte_settings]:on=展开,off=折叠
var xxljob_adminlte_settings = $.cookie('xxljob_adminlte_settings'); // on=open,off=close
if ('off' == xxljob_adminlte_settings) {
xxljob_adminlte_settings = 'on';
} else {
......@@ -83,11 +80,12 @@ $(function(){
}
$.cookie('xxljob_adminlte_settings', xxljob_adminlte_settings, { expires: 7 }); //$.cookie('the_cookie', '', { expires: -1 });
});
// 左侧菜单状态,js + cookie方式(遗弃)
// left menu status v1: js + cookie
/*
var xxljob_adminlte_settings = $.cookie('xxljob_adminlte_settings');
if (xxljob_adminlte_settings == 'off') {
$('body').addClass('sidebar-collapse');
$('body').addClass('sidebar-collapse');
}
*/
......
/**
* Created by xuxueli on 17/4/24.
*/
$(function () {
// 过滤时间
var _startDate = moment().subtract(1, 'months'); // 默认,最近一月
var _endDate = moment();
// filter Time
var rangesConf = {};
rangesConf[I18n.daterangepicker_ranges_today] = [moment().startOf('day'), moment().endOf('day')];
rangesConf[I18n.daterangepicker_ranges_yesterday] = [moment().subtract(1, 'days').startOf('day'), moment().subtract(1, 'days').endOf('day')];
rangesConf[I18n.daterangepicker_ranges_this_month] = [moment().startOf('month'), moment().endOf('month')];
rangesConf[I18n.daterangepicker_ranges_last_month] = [moment().subtract(1, 'months').startOf('month'), moment().subtract(1, 'months').endOf('month')];
rangesConf[I18n.daterangepicker_ranges_recent_week] = [moment().subtract(1, 'weeks').startOf('day'), moment().endOf('day')];
rangesConf[I18n.daterangepicker_ranges_recent_month] = [moment().subtract(1, 'months').startOf('day'), moment().endOf('day')];
$('#filterTime').daterangepicker({
autoApply:false,
singleDatePicker:false,
......@@ -16,36 +20,28 @@ $(function () {
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]
},
ranges: rangesConf,
locale : {
format: 'YYYY-MM-DD HH:mm:ss',
separator : ' - ',
customRangeLabel : '自定义',
applyLabel : '确定',
cancelLabel : '取消',
fromLabel : '起始时间',
toLabel : '结束时间',
daysOfWeek : [ '日', '一', '二', '三', '四', '五', '六' ],
monthNames : [ '一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月' ],
customRangeLabel : I18n.daterangepicker_custom_name ,
applyLabel : I18n.system_ok ,
cancelLabel : I18n.system_cancel ,
fromLabel : I18n.daterangepicker_custom_starttime ,
toLabel : I18n.daterangepicker_custom_endtime ,
daysOfWeek : I18n.daterangepicker_custom_daysofweek.split(',') , // '日', '一', '二', '三', '四', '五', '六'
monthNames : I18n.daterangepicker_custom_monthnames.split(',') , // '一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'
firstDay : 1
},
startDate:_startDate,
endDate: _endDate
startDate: rangesConf[I18n.daterangepicker_ranges_recent_month][0] ,
endDate: rangesConf[I18n.daterangepicker_ranges_recent_month][1]
}, function (start, end, label) {
freshChartDate(start, end);
});
freshChartDate(_startDate, _endDate);
freshChartDate(rangesConf[I18n.daterangepicker_ranges_recent_month][0], rangesConf[I18n.daterangepicker_ranges_recent_month][1]);
/**
* 刷新报表
* fresh Chart Date
*
* @param startDate
* @param endDate
......@@ -53,7 +49,7 @@ $(function () {
function freshChartDate(startDate, endDate) {
$.ajax({
type : 'POST',
url : base_url + '/triggerChartDate',
url : base_url + '/chartInfo',
data : {
'startDate':startDate.format('YYYY-MM-DD HH:mm:ss'),
'endDate':endDate.format('YYYY-MM-DD HH:mm:ss')
......@@ -65,8 +61,9 @@ $(function () {
pieChartInit(data);
} else {
layer.open({
title: '系统提示',
content: (data.msg || '调度报表数据加载异常'),
title: I18n.system_tips ,
btn: [ I18n.system_ok ],
content: (data.msg || I18n.job_dashboard_report_loaddata_fail ),
icon: '2'
});
}
......@@ -75,12 +72,12 @@ $(function () {
}
/**
* 折线图
* line Chart Init
*/
function lineChartInit(data) {
var option = {
title: {
text: '日期分布图'
text: I18n.job_dashboard_date_report
},
tooltip : {
trigger: 'axis',
......@@ -92,7 +89,7 @@ $(function () {
}
},
legend: {
data:['成功调度次数','失败调度次数']
data:[I18n.joblog_status_suc, I18n.joblog_status_fail, I18n.joblog_status_running]
},
toolbox: {
feature: {
......@@ -119,16 +116,16 @@ $(function () {
],
series : [
{
name:'成功调度次数',
name:I18n.joblog_status_suc,
type:'line',
stack: '总量',
stack: 'Total',
areaStyle: {normal: {}},
data: data.content.triggerDayCountSucList
},
{
name:'失败调度次数',
name:I18n.joblog_status_fail,
type:'line',
stack: '总量',
stack: 'Total',
label: {
normal: {
show: true,
......@@ -137,9 +134,16 @@ $(function () {
},
areaStyle: {normal: {}},
data: data.content.triggerDayCountFailList
},
{
name:I18n.joblog_status_running,
type:'line',
stack: 'Total',
areaStyle: {normal: {}},
data: data.content.triggerDayCountRunningList
}
],
color:['#00A65A', '#F39C12']
color:['#00A65A', '#c23632', '#F39C12']
};
var lineChart = echarts.init(document.getElementById('lineChart'));
......@@ -147,38 +151,42 @@ $(function () {
}
/**
* 饼图
* pie Chart Init
*/
function pieChartInit(data) {
var option = {
title : {
text: '成功比例图',
text: I18n.job_dashboard_rate_report ,
/*subtext: 'subtext',*/
x:'center'
},
tooltip : {
trigger: 'item',
formatter: "{a} <br/>{b} : {c} ({d}%)"
formatter: "{b} : {c} ({d}%)"
},
legend: {
orient: 'vertical',
left: 'left',
data: ['成功调度次数','失败调度次数']
data: [I18n.joblog_status_suc, I18n.joblog_status_fail, I18n.joblog_status_running ]
},
series : [
{
name: '分布比例',
//name: '分布比例',
type: 'pie',
radius : '55%',
center: ['50%', '60%'],
data:[
{
value:data.content.triggerCountSucTotal,
name:'成功调度次数'
name:I18n.joblog_status_suc,
value:data.content.triggerCountSucTotal
},
{
name:I18n.joblog_status_fail,
value:data.content.triggerCountFailTotal
},
{
value:data.content.triggerCountFailTotal,
name:'失败调度次数'
name:I18n.joblog_status_running,
value:data.content.triggerCountRunningTotal
}
],
itemStyle: {
......@@ -190,7 +198,7 @@ $(function () {
}
}
],
color:['#00A65A', '#F39C12']
color:['#00A65A', '#c23632', '#F39C12']
};
var pieChart = echarts.init(document.getElementById('pieChart'));
pieChart.setOption(option);
......
$(function() {
// init code editor
/*var codeEditor = CodeMirror.fromTextArea(document.getElementById("glueSource"), {
mode : "text/x-java",
lineNumbers : true,
matchBrackets : true
});*/
var codeEditor;
function initIde(glueSource) {
if (codeEditor == null) {
......@@ -44,16 +38,18 @@ $(function() {
if (!glueRemark) {
layer.open({
title: '系统提示',
content: '请输入备注',
title: I18n.system_tips,
btn: [ I18n.system_ok],
content: I18n.system_please_input + I18n.jobinfo_glue_remark ,
icon: '2'
});
return;
}
if (glueRemark.length <4 || glueRemark.length > 100) {
layer.open({
title: '系统提示',
content: '备注长度应该在4至100之间',
title: I18n.system_tips ,
btn: [ I18n.system_ok ],
content: I18n.jobinfo_glue_remark_limit ,
icon: '2'
});
return;
......@@ -71,8 +67,9 @@ $(function() {
success : function(data){
if (data.code == 200) {
layer.open({
title: '系统提示',
content: '保存成功',
title: I18n.system_tips,
btn: [ I18n.system_ok ],
content: (I18n.system_save + I18n.system_success) ,
icon: '1',
end: function(layero, index){
//$(window).unbind('beforeunload');
......@@ -81,8 +78,9 @@ $(function() {
});
} else {
layer.open({
title: '系统提示',
content: (data.msg || "保存失败"),
title: I18n.system_tips,
btn: [ I18n.system_ok ],
content: (data.msg || (I18n.system_save + I18n.system_fail) ),
icon: '2'
});
}
......
......@@ -4,7 +4,11 @@ $(function() {
$('.remove').on('click', function(){
var id = $(this).attr('id');
layer.confirm('确认删除分组?', {icon: 3, title:'系统提示'}, function(index){
layer.confirm( (I18n.system_ok + I18n.jobgroup_del + '?') , {
icon: 3,
title: I18n.system_tips ,
btn: [ I18n.system_ok, I18n.system_cancel ]
}, function(index){
layer.close(index);
$.ajax({
......@@ -15,8 +19,9 @@ $(function() {
success : function(data){
if (data.code == 200) {
layer.open({
title: '系统提示',
content: '删除成功',
title: I18n.system_tips ,
btn: [ I18n.system_ok ],
content: (I18n.jobgroup_del + I18n.system_success),
icon: '1',
end: function(layero, index){
window.location.reload();
......@@ -24,8 +29,9 @@ $(function() {
});
} else {
layer.open({
title: '系统提示',
content: (data.msg || "删除失败"),
title: I18n.system_tips,
btn: [ I18n.system_ok ],
content: (data.msg || (I18n.jobgroup_del + I18n.system_fail)),
icon: '2'
});
}
......@@ -35,12 +41,12 @@ $(function() {
});
// jquery.validate 自定义校验 “英文字母开头,只含有英文字母、数字和下划线
// jquery.validate “low letters start, limit contants、 letters、numbers and line-through.
jQuery.validator.addMethod("myValid01", function(value, element) {
var length = value.length;
var valid = /^[a-z][a-zA-Z0-9-]*$/;
return this.optional(element) || valid.test(value);
}, "限制以小写字母开头,由小写字母、数字和下划线组成");
}, I18n.jobgroup_field_appName_limit );
$('.add').on('click', function(){
$('#addModal').modal({backdrop: false, keyboard: false}).modal('show');
......@@ -67,18 +73,18 @@ $(function() {
},
messages : {
appName : {
required :"请输入“AppName”",
rangelength:"AppName长度限制为4~64",
myValid01: "限制以小写字母开头,由小写字母、数字和中划线组成"
required : I18n.system_please_input+"AppName",
rangelength: I18n.jobgroup_field_appName_length ,
myValid01: I18n.jobgroup_field_appName_limit
},
title : {
required :"请输入“执行器名称”",
rangelength:"长度限制为4~12"
required : I18n.system_please_input + I18n.jobgroup_field_title ,
rangelength: I18n.jobgroup_field_title_length
},
order : {
required :"请输入“排序”",
digits: "请输入整数",
range: "取值范围为1~1000"
required : I18n.system_please_input + I18n.jobgroup_field_order ,
digits: I18n.jobgroup_field_order_digits ,
range: I18n.jobgroup_field_orderrange
}
},
highlight : function(element) {
......@@ -96,8 +102,9 @@ $(function() {
if (data.code == "200") {
$('#addModal').modal('hide');
layer.open({
title: '系统提示',
content: '新增成功',
title: I18n.system_tips ,
btn: [ I18n.system_ok ],
content: I18n.system_add_suc ,
icon: '1',
end: function(layero, index){
window.location.reload();
......@@ -105,8 +112,9 @@ $(function() {
});
} else {
layer.open({
title: '系统提示',
content: (data.msg || "新增失败"),
title: I18n.system_tips,
btn: [ I18n.system_ok ],
content: (data.msg || I18n.system_add_fail ),
icon: '2'
});
}
......@@ -119,7 +127,7 @@ $(function() {
$("#addModal .form .form-group").removeClass("has-error");
});
// 注册方式,切换
// addressType change
$("#addModal input[name=addressType], #updateModal input[name=addressType]").click(function(){
var addressType = $(this).val();
var $addressList = $(this).parents("form").find("textarea[name=addressList]");
......@@ -171,20 +179,20 @@ $(function() {
}
},
messages : {
appName : {
required :"请输入“AppName”",
rangelength:"AppName长度限制为4~64",
myValid01: "限制以小写字母开头,由小写字母、数字和中划线组成"
},
title : {
required :"请输入“执行器名称”",
rangelength:"长度限制为4~12"
},
order : {
required :"请输入“排序”",
digits: "请输入整数",
range: "取值范围为1~1000"
}
appName : {
required : I18n.system_please_input+"AppName",
rangelength: I18n.jobgroup_field_appName_length ,
myValid01: I18n.jobgroup_field_appName_limit
},
title : {
required : I18n.system_please_input + I18n.jobgroup_field_title ,
rangelength: I18n.jobgroup_field_title_length
},
order : {
required : I18n.system_please_input + I18n.jobgroup_field_order ,
digits: I18n.jobgroup_field_order_digits ,
range: I18n.jobgroup_field_orderrange
}
},
highlight : function(element) {
$(element).closest('.form-group').addClass('has-error');
......@@ -202,8 +210,9 @@ $(function() {
$('#addModal').modal('hide');
layer.open({
title: '系统提示',
content: '更新成功',
title: I18n.system_tips ,
btn: [ I18n.system_ok ],
content: I18n.system_update_suc ,
icon: '1',
end: function(layero, index){
window.location.reload();
......@@ -211,8 +220,9 @@ $(function() {
});
} else {
layer.open({
title: '系统提示',
content: (data.msg || "更新失败"),
title: I18n.system_tips,
btn: [ I18n.system_ok ],
content: (data.msg || I18n.system_update_fail ),
icon: '2'
});
}
......
......@@ -3,7 +3,7 @@ $(function() {
// trigger fail, end
if (triggerCode != 200) {
$('#logConsoleRunning').hide();
$('#logConsole').append('<span style="color: red;">任务发起调度失败,无法查看执行日志</span>');
$('#logConsole').append('<span style="color: red;">'+ I18n.joblog_rolling_log_triggerfail +'</span>');
return;
}
......@@ -13,7 +13,7 @@ $(function() {
function pullLog() {
// pullFailCount, max=20
if (pullFailCount++ > 20) {
logRunStop('<span style="color: red;">终止请求Rolling日志,请求失败次数超上限,可刷新页面重新加载日志</span>');
logRunStop('<span style="color: red;">'+ I18n.joblog_rolling_log_failoften +'</span>');
return;
}
......
$(function(){
// 复选框
// input iCheck
$('input').iCheck({
checkboxClass: 'icheckbox_square-blue',
radioClass: 'iradio_square-blue',
increaseArea: '20%' // optional
});
// 登录.规则校验
// login Form Valid
var loginFormValid = $("#loginForm").validate({
errorElement : 'span',
errorClass : 'help-block',
......@@ -25,14 +26,13 @@ $(function(){
},
messages : {
userName : {
required :"请输入登录账号." ,
minlength:"登录账号不应低于5位",
maxlength:"登录账号不应超过18位"
},
required : I18n.login_username_empty,
minlength : I18n.login_username_lt_5
},
password : {
required :"请输入登录密码." ,
minlength:"登录密码不应低于5位",
maxlength:"登录密码不应超过18位"
required : I18n.login_password_empty ,
minlength : I18n.login_password_lt_5
/*,maxlength:"登录密码不应超过18位"*/
}
},
highlight : function(element) {
......@@ -48,22 +48,15 @@ $(function(){
submitHandler : function(form) {
$.post(base_url + "/login", $("#loginForm").serialize(), function(data, status) {
if (data.code == "200") {
layer.msg('登录成功');
layer.msg( I18n.login_success );
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: '系统提示',
content: (data.msg || "登录失败"),
title: I18n.system_tips,
btn: [ I18n.system_ok ],
content: (data.msg || I18n.login_fail ),
icon: '2'
});
}
......
/*! layer mobile-v2.0.0 Web弹层组件 MIT License http://layer.layui.com/mobile By 贤心 */
;!function(e){"use strict";var t=document,n="querySelectorAll",i="getElementsByClassName",a=function(e){return t[n](e)},s={type:0,shade:!0,shadeClose:!0,fixed:!0,anim:"scale"},l={extend:function(e){var t=JSON.parse(JSON.stringify(s));for(var n in e)t[n]=e[n];return t},timer:{},end:{}};l.touch=function(e,t){e.addEventListener("click",function(e){t.call(this,e)},!1)};var r=0,o=["layui-m-layer"],c=function(e){var t=this;t.config=l.extend(e),t.view()};c.prototype.view=function(){var e=this,n=e.config,s=t.createElement("div");e.id=s.id=o[0]+r,s.setAttribute("class",o[0]+" "+o[0]+(n.type||0)),s.setAttribute("index",r);var l=function(){var e="object"==typeof n.title;return n.title?'<h3 style="'+(e?n.title[1]:"")+'">'+(e?n.title[0]:n.title)+"</h3>":""}(),c=function(){"string"==typeof n.btn&&(n.btn=[n.btn]);var e,t=(n.btn||[]).length;return 0!==t&&n.btn?(e='<span yes type="1">'+n.btn[0]+"</span>",2===t&&(e='<span no type="0">'+n.btn[1]+"</span>"+e),'<div class="layui-m-layerbtn">'+e+"</div>"):""}();if(n.fixed||(n.top=n.hasOwnProperty("top")?n.top:100,n.style=n.style||"",n.style+=" top:"+(t.body.scrollTop+n.top)+"px"),2===n.type&&(n.content='<i></i><i class="layui-m-layerload"></i><i></i><p>'+(n.content||"")+"</p>"),n.skin&&(n.anim="up"),"msg"===n.skin&&(n.shade=!1),s.innerHTML=(n.shade?"<div "+("string"==typeof n.shade?'style="'+n.shade+'"':"")+' class="layui-m-layershade"></div>':"")+'<div class="layui-m-layermain" '+(n.fixed?"":'style="position:static;"')+'><div class="layui-m-layersection"><div class="layui-m-layerchild '+(n.skin?"layui-m-layer-"+n.skin+" ":"")+(n.className?n.className:"")+" "+(n.anim?"layui-m-anim-"+n.anim:"")+'" '+(n.style?'style="'+n.style+'"':"")+">"+l+'<div class="layui-m-layercont">'+n.content+"</div>"+c+"</div></div></div>",!n.type||2===n.type){var d=t[i](o[0]+n.type),y=d.length;y>=1&&layer.close(d[0].getAttribute("index"))}document.body.appendChild(s);var u=e.elem=a("#"+e.id)[0];n.success&&n.success(u),e.index=r++,e.action(n,u)},c.prototype.action=function(e,t){var n=this;e.time&&(l.timer[n.index]=setTimeout(function(){layer.close(n.index)},1e3*e.time));var a=function(){var t=this.getAttribute("type");0==t?(e.no&&e.no(),layer.close(n.index)):e.yes?e.yes(n.index):layer.close(n.index)};if(e.btn)for(var s=t[i]("layui-m-layerbtn")[0].children,r=s.length,o=0;o<r;o++)l.touch(s[o],a);if(e.shade&&e.shadeClose){var c=t[i]("layui-m-layershade")[0];l.touch(c,function(){layer.close(n.index,e.end)})}e.end&&(l.end[n.index]=e.end)},e.layer={v:"2.0",index:r,open:function(e){var t=new c(e||{});return t.index},close:function(e){var n=a("#"+o[0]+e)[0];n&&(n.innerHTML="",t.body.removeChild(n),clearTimeout(l.timer[e]),delete l.timer[e],"function"==typeof l.end[e]&&l.end[e](),delete l.end[e])},closeAll:function(){for(var e=t[i](o[0]),n=0,a=e.length;n<a;n++)layer.close(0|e[0].getAttribute("index"))}},"function"==typeof define?define(function(){return layer}):function(){var e=document.scripts,n=e[e.length-1],i=n.src,a=i.substring(0,i.lastIndexOf("/")+1);n.getAttribute("merge")||document.head.appendChild(function(){var e=t.createElement("link");return e.href=a+"need/layer.css?2.0",e.type="text/css",e.rel="styleSheet",e.id="layermcss",e}())}()}(window);
\ No newline at end of file
.layui-m-layer{position:relative;z-index:19891014}.layui-m-layer *{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}.layui-m-layermain,.layui-m-layershade{position:fixed;left:0;top:0;width:100%;height:100%}.layui-m-layershade{background-color:rgba(0,0,0,.7);pointer-events:auto}.layui-m-layermain{display:table;font-family:Helvetica,arial,sans-serif;pointer-events:none}.layui-m-layermain .layui-m-layersection{display:table-cell;vertical-align:middle;text-align:center}.layui-m-layerchild{position:relative;display:inline-block;text-align:left;background-color:#fff;font-size:14px;border-radius:5px;box-shadow:0 0 8px rgba(0,0,0,.1);pointer-events:auto;-webkit-overflow-scrolling:touch;-webkit-animation-fill-mode:both;animation-fill-mode:both;-webkit-animation-duration:.2s;animation-duration:.2s}@-webkit-keyframes layui-m-anim-scale{0%{opacity:0;-webkit-transform:scale(.5);transform:scale(.5)}100%{opacity:1;-webkit-transform:scale(1);transform:scale(1)}}@keyframes layui-m-anim-scale{0%{opacity:0;-webkit-transform:scale(.5);transform:scale(.5)}100%{opacity:1;-webkit-transform:scale(1);transform:scale(1)}}.layui-m-anim-scale{animation-name:layui-m-anim-scale;-webkit-animation-name:layui-m-anim-scale}@-webkit-keyframes layui-m-anim-up{0%{opacity:0;-webkit-transform:translateY(800px);transform:translateY(800px)}100%{opacity:1;-webkit-transform:translateY(0);transform:translateY(0)}}@keyframes layui-m-anim-up{0%{opacity:0;-webkit-transform:translateY(800px);transform:translateY(800px)}100%{opacity:1;-webkit-transform:translateY(0);transform:translateY(0)}}.layui-m-anim-up{-webkit-animation-name:layui-m-anim-up;animation-name:layui-m-anim-up}.layui-m-layer0 .layui-m-layerchild{width:90%;max-width:640px}.layui-m-layer1 .layui-m-layerchild{border:none;border-radius:0}.layui-m-layer2 .layui-m-layerchild{width:auto;max-width:260px;min-width:40px;border:none;background:0 0;box-shadow:none;color:#fff}.layui-m-layerchild h3{padding:0 10px;height:60px;line-height:60px;font-size:16px;font-weight:400;border-radius:5px 5px 0 0;text-align:center}.layui-m-layerbtn span,.layui-m-layerchild h3{text-overflow:ellipsis;overflow:hidden;white-space:nowrap}.layui-m-layercont{padding:50px 30px;line-height:22px;text-align:center}.layui-m-layer1 .layui-m-layercont{padding:0;text-align:left}.layui-m-layer2 .layui-m-layercont{text-align:center;padding:0;line-height:0}.layui-m-layer2 .layui-m-layercont i{width:25px;height:25px;margin-left:8px;display:inline-block;background-color:#fff;border-radius:100%;-webkit-animation:layui-m-anim-loading 1.4s infinite ease-in-out;animation:layui-m-anim-loading 1.4s infinite ease-in-out;-webkit-animation-fill-mode:both;animation-fill-mode:both}.layui-m-layerbtn,.layui-m-layerbtn span{position:relative;text-align:center;border-radius:0 0 5px 5px}.layui-m-layer2 .layui-m-layercont p{margin-top:20px}@-webkit-keyframes layui-m-anim-loading{0%,100%,80%{transform:scale(0);-webkit-transform:scale(0)}40%{transform:scale(1);-webkit-transform:scale(1)}}@keyframes layui-m-anim-loading{0%,100%,80%{transform:scale(0);-webkit-transform:scale(0)}40%{transform:scale(1);-webkit-transform:scale(1)}}.layui-m-layer2 .layui-m-layercont i:first-child{margin-left:0;-webkit-animation-delay:-.32s;animation-delay:-.32s}.layui-m-layer2 .layui-m-layercont i.layui-m-layerload{-webkit-animation-delay:-.16s;animation-delay:-.16s}.layui-m-layer2 .layui-m-layercont>div{line-height:22px;padding-top:7px;margin-bottom:20px;font-size:14px}.layui-m-layerbtn{display:box;display:-moz-box;display:-webkit-box;width:100%;height:50px;line-height:50px;font-size:0;border-top:1px solid #D0D0D0;background-color:#F2F2F2}.layui-m-layerbtn span{display:block;-moz-box-flex:1;box-flex:1;-webkit-box-flex:1;font-size:14px;cursor:pointer}.layui-m-layerbtn span[yes]{color:#40AFFE}.layui-m-layerbtn span[no]{border-right:1px solid #D0D0D0;border-radius:0 0 0 5px}.layui-m-layerbtn span:active{background-color:#F6F6F6}.layui-m-layerend{position:absolute;right:7px;top:10px;width:30px;height:30px;border:0;font-weight:400;background:0 0;cursor:pointer;-webkit-appearance:none;font-size:30px}.layui-m-layerend::after,.layui-m-layerend::before{position:absolute;left:5px;top:15px;content:'';width:18px;height:1px;background-color:#999;transform:rotate(45deg);-webkit-transform:rotate(45deg);border-radius:3px}.layui-m-layerend::after{transform:rotate(-45deg);-webkit-transform:rotate(-45deg)}body .layui-m-layer .layui-m-layer-footer{position:fixed;width:95%;max-width:100%;margin:0 auto;left:0;right:0;bottom:10px;background:0 0}.layui-m-layer-footer .layui-m-layercont{padding:20px;border-radius:5px 5px 0 0;background-color:rgba(255,255,255,.8)}.layui-m-layer-footer .layui-m-layerbtn{display:block;height:auto;background:0 0;border-top:none}.layui-m-layer-footer .layui-m-layerbtn span{background-color:rgba(255,255,255,.8)}.layui-m-layer-footer .layui-m-layerbtn span[no]{color:#FD482C;border-top:1px solid #c2c2c2;border-radius:0 0 5px 5px}.layui-m-layer-footer .layui-m-layerbtn span[yes]{margin-top:10px;border-radius:5px}body .layui-m-layer .layui-m-layer-msg{width:auto;max-width:90%;margin:0 auto;bottom:-150px;background-color:rgba(0,0,0,.7);color:#fff}.layui-m-layer-msg .layui-m-layercont{padding:10px 20px}
\ No newline at end of file
package com.xxl.job.admin.controller;
import com.xxl.job.admin.controller.interceptor.PermissionInterceptor;
import com.xxl.job.admin.core.util.PropertiesUtil;
import com.xxl.job.admin.core.conf.XxlJobAdminConfig;
import org.junit.Before;
import org.junit.Test;
import org.springframework.http.MediaType;
......@@ -22,8 +22,8 @@ public class JobInfoControllerTest extends AbstractSpringMvcTest {
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"))
.param("userName", XxlJobAdminConfig.getAdminConfig().getLoginUsername())
.param("password", XxlJobAdminConfig.getAdminConfig().getLoginPassword())
).andReturn();
cookie = ret.getResponse().getCookie(PermissionInterceptor.LOGIN_IDENTITY_KEY);
}
......
......@@ -20,8 +20,8 @@ public class XxlJobInfoDaoTest {
@Test
public void pageList(){
List<XxlJobInfo> list = xxlJobInfoDao.pageList(0, 20, 0, null);
int list_count = xxlJobInfoDao.pageListCount(0, 20, 0, null);
List<XxlJobInfo> list = xxlJobInfoDao.pageList(0, 20, 0, null, null);
int list_count = xxlJobInfoDao.pageListCount(0, 20, 0, null, null);
System.out.println(list);
System.out.println(list_count);
......
......@@ -50,7 +50,7 @@ public class XxlJobLogDaoTest {
dto = xxlJobLogDao.load(log.getId());
List<Map<String, Object>> list2 = xxlJobLogDao.triggerCountByDay(DateUtils.addDays(new Date(), 30), new Date(), 200);
List<Map<String, Object>> list2 = xxlJobLogDao.triggerCountByDay(DateUtils.addDays(new Date(), 30), new Date());
int ret4 = xxlJobLogDao.clearLog(1, 1, new Date(), 100);
......
package com.xxl.job.admin.util;
import com.xxl.job.admin.core.util.I18nUtil;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* email util test
*
* @author xuxueli 2017-12-22 17:16:23
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath*:spring/applicationcontext-*.xml")
public class I18nUtilTest {
@Test
public void test(){
System.out.println(I18nUtil.getString("admin_name"));
System.out.println(I18nUtil.getMultString("admin_name", "admin_name_full"));
System.out.println(I18nUtil.getMultString());
}
}
......@@ -2,6 +2,9 @@ package com.xxl.job.admin.util;
import com.xxl.job.admin.core.util.MailUtil;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import java.text.MessageFormat;
......@@ -10,6 +13,8 @@ import java.text.MessageFormat;
*
* @author xuxueli 2017-12-22 17:16:23
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath*:spring/applicationcontext-*.xml")
public class MailUtilTest {
@Test
......
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"));
}
}
......@@ -4,7 +4,7 @@
<parent>
<groupId>com.xuxueli</groupId>
<artifactId>xxl-job</artifactId>
<version>1.9.0-SNAPSHOT</version>
<version>1.9.2-SNAPSHOT</version>
</parent>
<artifactId>xxl-job-core</artifactId>
<packaging>jar</packaging>
......
......@@ -8,6 +8,7 @@ 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.JobLogFileCleanThread;
import com.xxl.job.core.thread.JobThread;
import com.xxl.job.core.util.NetUtil;
import org.slf4j.Logger;
......@@ -28,32 +29,35 @@ public class XxlJobExecutor implements ApplicationContextAware {
private static final Logger logger = LoggerFactory.getLogger(XxlJobExecutor.class);
// ---------------------- param ----------------------
private String adminAddresses;
private String appName;
private String ip;
private int port;
private String appName;
private String adminAddresses;
private String accessToken;
private String logPath;
private int logRetentionDays;
public void setAdminAddresses(String adminAddresses) {
this.adminAddresses = adminAddresses;
}
public void setAppName(String appName) {
this.appName = appName;
}
public void setIp(String ip) {
this.ip = ip;
}
public void setPort(int port) {
this.port = port;
}
public void setAppName(String appName) {
this.appName = appName;
}
public void setAdminAddresses(String adminAddresses) {
this.adminAddresses = adminAddresses;
}
public void setAccessToken(String accessToken) {
this.accessToken = accessToken;
}
public void setLogPath(String logPath) {
this.logPath = logPath;
}
public void setLogRetentionDays(int logRetentionDays) {
this.logRetentionDays = logRetentionDays;
}
// ---------------------- applicationContext ----------------------
private static ApplicationContext applicationContext;
......@@ -79,6 +83,9 @@ public class XxlJobExecutor implements ApplicationContextAware {
// init executor-server
initExecutorServer(port, ip, appName, accessToken);
// init JobLogFileCleanThread
JobLogFileCleanThread.getInstance().start(logRetentionDays);
}
public void destroy(){
// destory JobThreadRepository
......@@ -91,6 +98,9 @@ public class XxlJobExecutor implements ApplicationContextAware {
// destory executor-server
stopExecutorServer();
// destory JobLogFileCleanThread
JobLogFileCleanThread.getInstance().toStop();
}
......
......@@ -40,8 +40,8 @@ public class ScriptJobHandler extends IJobHandler {
String cmd = glueType.getCmd();
// make script file
String scriptFileName = XxlJobFileAppender.getLogPath()
.concat("/gluesource/")
String scriptFileName = XxlJobFileAppender.getGlueSrcPath()
.concat("/")
.concat(String.valueOf(jobId))
.concat("_")
.concat(String.valueOf(glueUpdatetime))
......
......@@ -20,8 +20,21 @@ public class XxlJobFileAppender {
public static final InheritableThreadLocal<String> contextHolder = new InheritableThreadLocal<String>();
// log base path
/**
* log base path
*
* strut like:
* ---/
* ---/gluesource/
* ---/gluesource/10_1514171108000.js
* ---/gluesource/10_1514171108000.js
* ---/2017-12-25/
* ---/2017-12-25/639.log
* ---/2017-12-25/821.log
*
*/
private static String logBasePath = "/data/applogs/xxl-job/jobhandler";
private static String glueSrcPath = logBasePath.concat("/gluesource");
public static void initLogPath(String logPath){
// init
if (logPath!=null && logPath.trim().length()>0) {
......@@ -39,11 +52,14 @@ public class XxlJobFileAppender {
if (!glueBaseDir.exists()) {
glueBaseDir.mkdirs();
}
glueSrcPath = glueBaseDir.getPath();
}
public static String getLogPath() {
return logBasePath;
}
public static String getGlueSrcPath() {
return glueSrcPath;
}
/**
* log filename, like "logPath/yyyy-MM-dd/9999.log"
......
package com.xxl.job.core.thread;
import com.xxl.job.core.log.XxlJobFileAppender;
import com.xxl.job.core.util.FileUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.concurrent.TimeUnit;
/**
* job file clean thread
*
* @author xuxueli 2017-12-29 16:23:43
*/
public class JobLogFileCleanThread extends Thread {
private static Logger logger = LoggerFactory.getLogger(JobLogFileCleanThread.class);
private static JobLogFileCleanThread instance = new JobLogFileCleanThread();
public static JobLogFileCleanThread getInstance(){
return instance;
}
private Thread localThread;
private volatile boolean toStop = false;
public void start(final long logRetentionDays){
// limit min value
if (logRetentionDays < 3 ) {
return;
}
localThread = new Thread(new Runnable() {
@Override
public void run() {
while (!toStop) {
try {
// clean log dir, over logRetentionDays
File[] childDirs = new File(XxlJobFileAppender.getLogPath()).listFiles();
if (childDirs!=null && childDirs.length>0) {
// today
Calendar todayCal = Calendar.getInstance();
todayCal.set(Calendar.HOUR_OF_DAY,0);
todayCal.set(Calendar.MINUTE,0);
todayCal.set(Calendar.SECOND,0);
todayCal.set(Calendar.MILLISECOND,0);
Date todayDate = todayCal.getTime();
for (File childFile: childDirs) {
// valid
if (!childFile.isDirectory()) {
continue;
}
if (childFile.getName().indexOf("-") == -1) {
continue;
}
// file create date
Date logFileCreateDate = null;
try {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
logFileCreateDate = simpleDateFormat.parse(childFile.getName());
} catch (ParseException e) {
logger.error(e.getMessage(), e);
}
if (logFileCreateDate == null) {
continue;
}
if ((todayDate.getTime()-logFileCreateDate.getTime()) >= logRetentionDays * (24 * 60 * 60 * 1000) ) {
FileUtil.deleteRecursively(childFile);
}
}
}
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
try {
TimeUnit.DAYS.sleep(1);
} catch (InterruptedException e) {
logger.error(e.getMessage(), e);
}
}
logger.info(">>>>>>>>>>> xxl-job, executor JobLogFileCleanThread thread destory.");
}
});
localThread.setDaemon(true);
localThread.start();
}
public void toStop() {
toStop = true;
if (localThread == null) {
return;
}
// interrupt and wait
localThread.interrupt();
try {
localThread.join();
} catch (InterruptedException e) {
logger.error(e.getMessage(), e);
}
}
}
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论