Преглед на файлове

精简项目,取消第三方依赖 ommons-lang3

xuxueli преди 6 години
родител
ревизия
9d92fa7578

+ 2 - 1
doc/XXL-JOB官方文档.md Целия файл

1432
 - 8、调度中心告警邮件发送组件改为 “spring-boot-starter-mail”;
1432
 - 8、调度中心告警邮件发送组件改为 “spring-boot-starter-mail”;
1433
 - 9、记住密码功能优化,选中时永久记住;非选中时关闭浏览器即登出;
1433
 - 9、记住密码功能优化,选中时永久记住;非选中时关闭浏览器即登出;
1434
 - 10、项目依赖升级至较新稳定版本,如quartz、spring、jackson、groovy、xxl-rpc等等;
1434
 - 10、项目依赖升级至较新稳定版本,如quartz、spring、jackson、groovy、xxl-rpc等等;
1435
-- 11、精简项目,取消第三方依赖,如 commons-collections4 ;
1435
+- 11、精简项目,取消第三方依赖,如 commons-collections4、commons-lang3 ;
1436
 - 12、执行器回调日志落盘方案复用RPC序列化方案,并移除Jackson依赖;
1436
 - 12、执行器回调日志落盘方案复用RPC序列化方案,并移除Jackson依赖;
1437
 - 13、底层Log调优,应用正常终止取消异常栈信息打印;
1437
 - 13、底层Log调优,应用正常终止取消异常栈信息打印;
1438
 - 14、交互优化,尽量避免新开页面窗口;仅WebIDE支持新开页,并提供窗口快速关闭按钮;任务启、停、删除、触发等轻操作提示改为toast方式,
1438
 - 14、交互优化,尽量避免新开页面窗口;仅WebIDE支持新开页,并提供窗口快速关闭按钮;任务启、停、删除、触发等轻操作提示改为toast方式,
1493
 - 34、批量调度:调度请求入queue,调度线程批量获取调度请求并发起远程调度;提高线程效率;
1493
 - 34、批量调度:调度请求入queue,调度线程批量获取调度请求并发起远程调度;提高线程效率;
1494
 - 35、执行器端口复用,http通讯自动添加相对地址 "/xxl-job":
1494
 - 35、执行器端口复用,http通讯自动添加相对地址 "/xxl-job":
1495
 - 36、多语言执行器:约定跨语言通讯方案,以及通讯接口;
1495
 - 36、多语言执行器:约定跨语言通讯方案,以及通讯接口;
1496
+- 37、移除commons-exec,采用原生实现;
1496
 
1497
 
1497
 
1498
 
1498
 ## 七、其他
1499
 ## 七、其他

+ 0 - 1
pom.xml Целия файл

37
 		<junit.version>4.12</junit.version>
37
 		<junit.version>4.12</junit.version>
38
 
38
 
39
 		<commons-exec.version>1.3</commons-exec.version>
39
 		<commons-exec.version>1.3</commons-exec.version>
40
-		<commons-lang3.version>3.8.1</commons-lang3.version>
41
 
40
 
42
 		<groovy.version>2.5.6</groovy.version>
41
 		<groovy.version>2.5.6</groovy.version>
43
 		<quartz.version>2.3.1</quartz.version>
42
 		<quartz.version>2.3.1</quartz.version>

+ 0 - 8
xxl-job-admin/pom.xml Целия файл

60
 			<version>${mysql-connector-java.version}</version>
60
 			<version>${mysql-connector-java.version}</version>
61
 		</dependency>
61
 		</dependency>
62
 
62
 
63
-		<!-- commons-lang3 -->
64
-		<dependency>
65
-			<groupId>org.apache.commons</groupId>
66
-			<artifactId>commons-lang3</artifactId>
67
-			<version>${commons-lang3.version}</version>
68
-		</dependency>
69
-
70
-
71
 		<!-- quartz :quartz-2.2.3/c3p0-0.9.1.1/slf4j-api-1.6.6 -->
63
 		<!-- quartz :quartz-2.2.3/c3p0-0.9.1.1/slf4j-api-1.6.6 -->
72
 		<dependency>
64
 		<dependency>
73
 			<groupId>org.quartz-scheduler</groupId>
65
 			<groupId>org.quartz-scheduler</groupId>

+ 2 - 3
xxl-job-admin/src/main/java/com/xxl/job/admin/controller/IndexController.java Целия файл

5
 import com.xxl.job.admin.core.util.I18nUtil;
5
 import com.xxl.job.admin.core.util.I18nUtil;
6
 import com.xxl.job.admin.service.XxlJobService;
6
 import com.xxl.job.admin.service.XxlJobService;
7
 import com.xxl.job.core.biz.model.ReturnT;
7
 import com.xxl.job.core.biz.model.ReturnT;
8
-import org.apache.commons.lang3.StringUtils;
9
 import org.springframework.beans.propertyeditors.CustomDateEditor;
8
 import org.springframework.beans.propertyeditors.CustomDateEditor;
10
 import org.springframework.stereotype.Controller;
9
 import org.springframework.stereotype.Controller;
11
 import org.springframework.ui.Model;
10
 import org.springframework.ui.Model;
67
 		}
66
 		}
68
 
67
 
69
 		// param
68
 		// param
70
-		if (StringUtils.isBlank(userName) || StringUtils.isBlank(password)){
69
+		if (userName==null || userName.trim().length()==0 || password==null || password.trim().length()==0){
71
 			return new ReturnT<String>(500, I18nUtil.getString("login_param_empty"));
70
 			return new ReturnT<String>(500, I18nUtil.getString("login_param_empty"));
72
 		}
71
 		}
73
-		boolean ifRem = (StringUtils.isNotBlank(ifRemember) && "on".equals(ifRemember))?true:false;
72
+		boolean ifRem = (ifRemember!=null && ifRemember.trim().length()>0 && "on".equals(ifRemember))?true:false;
74
 
73
 
75
 		// do login
74
 		// do login
76
 		boolean loginRet = PermissionInterceptor.login(response, userName, password, ifRem);
75
 		boolean loginRet = PermissionInterceptor.login(response, userName, password, ifRem);

+ 13 - 10
xxl-job-admin/src/main/java/com/xxl/job/admin/controller/JobGroupController.java Целия файл

8
 import com.xxl.job.admin.dao.XxlJobInfoDao;
8
 import com.xxl.job.admin.dao.XxlJobInfoDao;
9
 import com.xxl.job.core.biz.model.ReturnT;
9
 import com.xxl.job.core.biz.model.ReturnT;
10
 import com.xxl.job.core.enums.RegistryConfig;
10
 import com.xxl.job.core.enums.RegistryConfig;
11
-import org.apache.commons.lang3.StringUtils;
12
 import org.springframework.stereotype.Controller;
11
 import org.springframework.stereotype.Controller;
13
 import org.springframework.ui.Model;
12
 import org.springframework.ui.Model;
14
 import org.springframework.web.bind.annotation.RequestMapping;
13
 import org.springframework.web.bind.annotation.RequestMapping;
48
 	public ReturnT<String> save(XxlJobGroup xxlJobGroup){
47
 	public ReturnT<String> save(XxlJobGroup xxlJobGroup){
49
 
48
 
50
 		// valid
49
 		// valid
51
-		if (xxlJobGroup.getAppName()==null || StringUtils.isBlank(xxlJobGroup.getAppName())) {
50
+		if (xxlJobGroup.getAppName()==null || xxlJobGroup.getAppName().trim().length()==0) {
52
 			return new ReturnT<String>(500, (I18nUtil.getString("system_please_input")+"AppName") );
51
 			return new ReturnT<String>(500, (I18nUtil.getString("system_please_input")+"AppName") );
53
 		}
52
 		}
54
 		if (xxlJobGroup.getAppName().length()<4 || xxlJobGroup.getAppName().length()>64) {
53
 		if (xxlJobGroup.getAppName().length()<4 || xxlJobGroup.getAppName().length()>64) {
55
 			return new ReturnT<String>(500, I18nUtil.getString("jobgroup_field_appName_length") );
54
 			return new ReturnT<String>(500, I18nUtil.getString("jobgroup_field_appName_length") );
56
 		}
55
 		}
57
-		if (xxlJobGroup.getTitle()==null || StringUtils.isBlank(xxlJobGroup.getTitle())) {
56
+		if (xxlJobGroup.getTitle()==null || xxlJobGroup.getTitle().trim().length()==0) {
58
 			return new ReturnT<String>(500, (I18nUtil.getString("system_please_input") + I18nUtil.getString("jobgroup_field_title")) );
57
 			return new ReturnT<String>(500, (I18nUtil.getString("system_please_input") + I18nUtil.getString("jobgroup_field_title")) );
59
 		}
58
 		}
60
 		if (xxlJobGroup.getAddressType()!=0) {
59
 		if (xxlJobGroup.getAddressType()!=0) {
61
-			if (StringUtils.isBlank(xxlJobGroup.getAddressList())) {
60
+			if (xxlJobGroup.getAddressList()==null || xxlJobGroup.getAddressList().trim().length()==0) {
62
 				return new ReturnT<String>(500, I18nUtil.getString("jobgroup_field_addressType_limit") );
61
 				return new ReturnT<String>(500, I18nUtil.getString("jobgroup_field_addressType_limit") );
63
 			}
62
 			}
64
 			String[] addresss = xxlJobGroup.getAddressList().split(",");
63
 			String[] addresss = xxlJobGroup.getAddressList().split(",");
65
 			for (String item: addresss) {
64
 			for (String item: addresss) {
66
-				if (StringUtils.isBlank(item)) {
65
+				if (item==null || item.trim().length()==0) {
67
 					return new ReturnT<String>(500, I18nUtil.getString("jobgroup_field_registryList_unvalid") );
66
 					return new ReturnT<String>(500, I18nUtil.getString("jobgroup_field_registryList_unvalid") );
68
 				}
67
 				}
69
 			}
68
 			}
77
 	@ResponseBody
76
 	@ResponseBody
78
 	public ReturnT<String> update(XxlJobGroup xxlJobGroup){
77
 	public ReturnT<String> update(XxlJobGroup xxlJobGroup){
79
 		// valid
78
 		// valid
80
-		if (xxlJobGroup.getAppName()==null || StringUtils.isBlank(xxlJobGroup.getAppName())) {
79
+		if (xxlJobGroup.getAppName()==null || xxlJobGroup.getAppName().trim().length()==0) {
81
 			return new ReturnT<String>(500, (I18nUtil.getString("system_please_input")+"AppName") );
80
 			return new ReturnT<String>(500, (I18nUtil.getString("system_please_input")+"AppName") );
82
 		}
81
 		}
83
 		if (xxlJobGroup.getAppName().length()<4 || xxlJobGroup.getAppName().length()>64) {
82
 		if (xxlJobGroup.getAppName().length()<4 || xxlJobGroup.getAppName().length()>64) {
84
 			return new ReturnT<String>(500, I18nUtil.getString("jobgroup_field_appName_length") );
83
 			return new ReturnT<String>(500, I18nUtil.getString("jobgroup_field_appName_length") );
85
 		}
84
 		}
86
-		if (xxlJobGroup.getTitle()==null || StringUtils.isBlank(xxlJobGroup.getTitle())) {
85
+		if (xxlJobGroup.getTitle()==null || xxlJobGroup.getTitle().trim().length()==0) {
87
 			return new ReturnT<String>(500, (I18nUtil.getString("system_please_input") + I18nUtil.getString("jobgroup_field_title")) );
86
 			return new ReturnT<String>(500, (I18nUtil.getString("system_please_input") + I18nUtil.getString("jobgroup_field_title")) );
88
 		}
87
 		}
89
 		if (xxlJobGroup.getAddressType() == 0) {
88
 		if (xxlJobGroup.getAddressType() == 0) {
92
 			String addressListStr = null;
91
 			String addressListStr = null;
93
 			if (registryList!=null && !registryList.isEmpty()) {
92
 			if (registryList!=null && !registryList.isEmpty()) {
94
 				Collections.sort(registryList);
93
 				Collections.sort(registryList);
95
-				addressListStr = StringUtils.join(registryList, ",");
94
+				addressListStr = "";
95
+				for (String item:registryList) {
96
+					addressListStr += item + ",";
97
+				}
98
+				addressListStr = addressListStr.substring(0, addressListStr.length()-1);
96
 			}
99
 			}
97
 			xxlJobGroup.setAddressList(addressListStr);
100
 			xxlJobGroup.setAddressList(addressListStr);
98
 		} else {
101
 		} else {
99
 			// 1=手动录入
102
 			// 1=手动录入
100
-			if (StringUtils.isBlank(xxlJobGroup.getAddressList())) {
103
+			if (xxlJobGroup.getAddressList()==null || xxlJobGroup.getAddressList().trim().length()==0) {
101
 				return new ReturnT<String>(500, I18nUtil.getString("jobgroup_field_addressType_limit") );
104
 				return new ReturnT<String>(500, I18nUtil.getString("jobgroup_field_addressType_limit") );
102
 			}
105
 			}
103
 			String[] addresss = xxlJobGroup.getAddressList().split(",");
106
 			String[] addresss = xxlJobGroup.getAddressList().split(",");
104
 			for (String item: addresss) {
107
 			for (String item: addresss) {
105
-				if (StringUtils.isBlank(item)) {
108
+				if (item==null || item.trim().length()==0) {
106
 					return new ReturnT<String>(500, I18nUtil.getString("jobgroup_field_registryList_unvalid") );
109
 					return new ReturnT<String>(500, I18nUtil.getString("jobgroup_field_registryList_unvalid") );
107
 				}
110
 				}
108
 			}
111
 			}

+ 8 - 11
xxl-job-admin/src/main/java/com/xxl/job/admin/controller/JobLogController.java Целия файл

11
 import com.xxl.job.core.biz.ExecutorBiz;
11
 import com.xxl.job.core.biz.ExecutorBiz;
12
 import com.xxl.job.core.biz.model.LogResult;
12
 import com.xxl.job.core.biz.model.LogResult;
13
 import com.xxl.job.core.biz.model.ReturnT;
13
 import com.xxl.job.core.biz.model.ReturnT;
14
-import org.apache.commons.lang3.StringUtils;
15
-import org.apache.commons.lang3.time.DateUtils;
14
+import com.xxl.job.core.util.DateUtil;
16
 import org.slf4j.Logger;
15
 import org.slf4j.Logger;
17
 import org.slf4j.LoggerFactory;
16
 import org.slf4j.LoggerFactory;
18
 import org.springframework.stereotype.Controller;
17
 import org.springframework.stereotype.Controller;
76
 		// parse param
75
 		// parse param
77
 		Date triggerTimeStart = null;
76
 		Date triggerTimeStart = null;
78
 		Date triggerTimeEnd = null;
77
 		Date triggerTimeEnd = null;
79
-		if (StringUtils.isNotBlank(filterTime)) {
78
+		if (filterTime!=null && filterTime.trim().length()>0) {
80
 			String[] temp = filterTime.split(" - ");
79
 			String[] temp = filterTime.split(" - ");
81
 			if (temp!=null && temp.length == 2) {
80
 			if (temp!=null && temp.length == 2) {
82
-				try {
83
-					triggerTimeStart = DateUtils.parseDate(temp[0], new String[]{"yyyy-MM-dd HH:mm:ss"});
84
-					triggerTimeEnd = DateUtils.parseDate(temp[1], new String[]{"yyyy-MM-dd HH:mm:ss"});
85
-				} catch (ParseException e) {	}
81
+				triggerTimeStart = DateUtil.parseDateTime(temp[0]);
82
+				triggerTimeEnd = DateUtil.parseDateTime(temp[1]);
86
 			}
83
 			}
87
 		}
84
 		}
88
 		
85
 		
179
 		Date clearBeforeTime = null;
176
 		Date clearBeforeTime = null;
180
 		int clearBeforeNum = 0;
177
 		int clearBeforeNum = 0;
181
 		if (type == 1) {
178
 		if (type == 1) {
182
-			clearBeforeTime = DateUtils.addMonths(new Date(), -1);	// 清理一个月之前日志数据
179
+			clearBeforeTime = DateUtil.addMonths(new Date(), -1);	// 清理一个月之前日志数据
183
 		} else if (type == 2) {
180
 		} else if (type == 2) {
184
-			clearBeforeTime = DateUtils.addMonths(new Date(), -3);	// 清理三个月之前日志数据
181
+			clearBeforeTime = DateUtil.addMonths(new Date(), -3);	// 清理三个月之前日志数据
185
 		} else if (type == 3) {
182
 		} else if (type == 3) {
186
-			clearBeforeTime = DateUtils.addMonths(new Date(), -6);	// 清理六个月之前日志数据
183
+			clearBeforeTime = DateUtil.addMonths(new Date(), -6);	// 清理六个月之前日志数据
187
 		} else if (type == 4) {
184
 		} else if (type == 4) {
188
-			clearBeforeTime = DateUtils.addYears(new Date(), -1);	// 清理一年之前日志数据
185
+			clearBeforeTime = DateUtil.addYears(new Date(), -1);	// 清理一年之前日志数据
189
 		} else if (type == 5) {
186
 		} else if (type == 5) {
190
 			clearBeforeNum = 1000;		// 清理一千条以前日志数据
187
 			clearBeforeNum = 1000;		// 清理一千条以前日志数据
191
 		} else if (type == 6) {
188
 		} else if (type == 6) {

+ 1 - 2
xxl-job-admin/src/main/java/com/xxl/job/admin/controller/interceptor/CookieInterceptor.java Целия файл

2
 
2
 
3
 import com.xxl.job.admin.core.util.FtlUtil;
3
 import com.xxl.job.admin.core.util.FtlUtil;
4
 import com.xxl.job.admin.core.util.I18nUtil;
4
 import com.xxl.job.admin.core.util.I18nUtil;
5
-import org.apache.commons.lang3.ArrayUtils;
6
 import org.springframework.stereotype.Component;
5
 import org.springframework.stereotype.Component;
7
 import org.springframework.web.servlet.ModelAndView;
6
 import org.springframework.web.servlet.ModelAndView;
8
 import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
7
 import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
25
 			ModelAndView modelAndView) throws Exception {
24
 			ModelAndView modelAndView) throws Exception {
26
 
25
 
27
 		// cookie
26
 		// cookie
28
-		if (modelAndView!=null && ArrayUtils.isNotEmpty(request.getCookies())) {
27
+		if (modelAndView!=null && request.getCookies()!=null && request.getCookies().length>0) {
29
 			HashMap<String, Cookie> cookieMap = new HashMap<String, Cookie>();
28
 			HashMap<String, Cookie> cookieMap = new HashMap<String, Cookie>();
30
 			for (Cookie ck : request.getCookies()) {
29
 			for (Cookie ck : request.getCookies()) {
31
 				cookieMap.put(ck.getName(), ck);
30
 				cookieMap.put(ck.getName(), ck);

+ 1 - 3
xxl-job-admin/src/main/java/com/xxl/job/admin/core/model/XxlJobGroup.java Целия файл

1
 package com.xxl.job.admin.core.model;
1
 package com.xxl.job.admin.core.model;
2
 
2
 
3
-import org.apache.commons.lang3.StringUtils;
4
-
5
 import java.util.ArrayList;
3
 import java.util.ArrayList;
6
 import java.util.Arrays;
4
 import java.util.Arrays;
7
 import java.util.List;
5
 import java.util.List;
21
     // registry list
19
     // registry list
22
     private List<String> registryList;  // 执行器地址列表(系统注册)
20
     private List<String> registryList;  // 执行器地址列表(系统注册)
23
     public List<String> getRegistryList() {
21
     public List<String> getRegistryList() {
24
-        if (StringUtils.isNotBlank(addressList)) {
22
+        if (addressList!=null && addressList.trim().length()>0) {
25
             registryList = new ArrayList<String>(Arrays.asList(addressList.split(",")));
23
             registryList = new ArrayList<String>(Arrays.asList(addressList.split(",")));
26
         }
24
         }
27
         return registryList;
25
         return registryList;

+ 5 - 2
xxl-job-admin/src/main/java/com/xxl/job/admin/core/thread/JobRegistryMonitorHelper.java Целия файл

4
 import com.xxl.job.admin.core.model.XxlJobGroup;
4
 import com.xxl.job.admin.core.model.XxlJobGroup;
5
 import com.xxl.job.admin.core.model.XxlJobRegistry;
5
 import com.xxl.job.admin.core.model.XxlJobRegistry;
6
 import com.xxl.job.core.enums.RegistryConfig;
6
 import com.xxl.job.core.enums.RegistryConfig;
7
-import org.apache.commons.lang3.StringUtils;
8
 import org.slf4j.Logger;
7
 import org.slf4j.Logger;
9
 import org.slf4j.LoggerFactory;
8
 import org.slf4j.LoggerFactory;
10
 
9
 
67
 								String addressListStr = null;
66
 								String addressListStr = null;
68
 								if (registryList!=null && !registryList.isEmpty()) {
67
 								if (registryList!=null && !registryList.isEmpty()) {
69
 									Collections.sort(registryList);
68
 									Collections.sort(registryList);
70
-									addressListStr = StringUtils.join(registryList, ",");
69
+									addressListStr = "";
70
+									for (String item:registryList) {
71
+										addressListStr += item + ",";
72
+									}
73
+									addressListStr = addressListStr.substring(0, addressListStr.length()-1);
71
 								}
74
 								}
72
 								group.setAddressList(addressListStr);
75
 								group.setAddressList(addressListStr);
73
 								XxlJobAdminConfig.getAdminConfig().getXxlJobGroupDao().update(group);
76
 								XxlJobAdminConfig.getAdminConfig().getXxlJobGroupDao().update(group);

+ 10 - 2
xxl-job-admin/src/main/java/com/xxl/job/admin/core/trigger/XxlJobTrigger.java Целия файл

13
 import com.xxl.job.core.enums.ExecutorBlockStrategyEnum;
13
 import com.xxl.job.core.enums.ExecutorBlockStrategyEnum;
14
 import com.xxl.rpc.util.IpUtil;
14
 import com.xxl.rpc.util.IpUtil;
15
 import com.xxl.rpc.util.ThrowableUtil;
15
 import com.xxl.rpc.util.ThrowableUtil;
16
-import org.apache.commons.lang3.StringUtils;
17
 import org.slf4j.Logger;
16
 import org.slf4j.Logger;
18
 import org.slf4j.LoggerFactory;
17
 import org.slf4j.LoggerFactory;
19
 
18
 
56
         int[] shardingParam = null;
55
         int[] shardingParam = null;
57
         if (executorShardingParam!=null){
56
         if (executorShardingParam!=null){
58
             String[] shardingArr = executorShardingParam.split("/");
57
             String[] shardingArr = executorShardingParam.split("/");
59
-            if (shardingArr.length==2 && StringUtils.isNumeric(shardingArr[0]) && StringUtils.isNumeric(shardingArr[1])) {
58
+            if (shardingArr.length==2 && isNumeric(shardingArr[0]) && isNumeric(shardingArr[1])) {
60
                 shardingParam = new int[2];
59
                 shardingParam = new int[2];
61
                 shardingParam[0] = Integer.valueOf(shardingArr[0]);
60
                 shardingParam[0] = Integer.valueOf(shardingArr[0]);
62
                 shardingParam[1] = Integer.valueOf(shardingArr[1]);
61
                 shardingParam[1] = Integer.valueOf(shardingArr[1]);
77
 
76
 
78
     }
77
     }
79
 
78
 
79
+    private static boolean isNumeric(String str){
80
+        try {
81
+            int result = Integer.valueOf(str);
82
+            return true;
83
+        } catch (NumberFormatException e) {
84
+            return false;
85
+        }
86
+    }
87
+
80
     /**
88
     /**
81
      * @param group                     job group, registry list may be empty
89
      * @param group                     job group, registry list may be empty
82
      * @param jobInfo
90
      * @param jobInfo

+ 1 - 2
xxl-job-admin/src/main/java/com/xxl/job/admin/core/util/I18nUtil.java Целия файл

1
 package com.xxl.job.admin.core.util;
1
 package com.xxl.job.admin.core.util;
2
 
2
 
3
 import com.xxl.job.admin.core.conf.XxlJobAdminConfig;
3
 import com.xxl.job.admin.core.conf.XxlJobAdminConfig;
4
-import org.apache.commons.lang3.StringUtils;
5
 import org.slf4j.Logger;
4
 import org.slf4j.Logger;
6
 import org.slf4j.LoggerFactory;
5
 import org.slf4j.LoggerFactory;
7
 import org.springframework.core.io.ClassPathResource;
6
 import org.springframework.core.io.ClassPathResource;
31
         try {
30
         try {
32
             // build i18n prop
31
             // build i18n prop
33
             String i18n = XxlJobAdminConfig.getAdminConfig().getI18n();
32
             String i18n = XxlJobAdminConfig.getAdminConfig().getI18n();
34
-            i18n = StringUtils.isNotBlank(i18n)?("_"+i18n):i18n;
33
+            i18n = (i18n!=null && i18n.trim().length()>0)?("_"+i18n):i18n;
35
             String i18nFile = MessageFormat.format("i18n/message{0}.properties", i18n);
34
             String i18nFile = MessageFormat.format("i18n/message{0}.properties", i18n);
36
 
35
 
37
             // load prop
36
             // load prop

+ 3 - 5
xxl-job-admin/src/main/java/com/xxl/job/admin/core/util/LocalCacheUtil.java Целия файл

1
 package com.xxl.job.admin.core.util;
1
 package com.xxl.job.admin.core.util;
2
 
2
 
3
-import org.apache.commons.lang3.StringUtils;
4
-
5
 import java.util.concurrent.ConcurrentHashMap;
3
 import java.util.concurrent.ConcurrentHashMap;
6
 import java.util.concurrent.ConcurrentMap;
4
 import java.util.concurrent.ConcurrentMap;
7
 
5
 
67
         cleanTimeutCache();
65
         cleanTimeutCache();
68
 
66
 
69
         // set new cache
67
         // set new cache
70
-        if (StringUtils.isBlank(key)) {
68
+        if (key==null || key.trim().length()==0) {
71
             return false;
69
             return false;
72
         }
70
         }
73
         if (val == null) {
71
         if (val == null) {
89
      * @return
87
      * @return
90
      */
88
      */
91
     public static boolean remove(String key){
89
     public static boolean remove(String key){
92
-        if (StringUtils.isBlank(key)) {
90
+        if (key==null || key.trim().length()==0) {
93
             return false;
91
             return false;
94
         }
92
         }
95
         cacheRepository.remove(key);
93
         cacheRepository.remove(key);
103
      * @return
101
      * @return
104
      */
102
      */
105
     public static Object get(String key){
103
     public static Object get(String key){
106
-        if (StringUtils.isBlank(key)) {
104
+        if (key==null || key.trim().length()==0) {
107
             return null;
105
             return null;
108
         }
106
         }
109
         LocalCacheData localCacheData = cacheRepository.get(key);
107
         LocalCacheData localCacheData = cacheRepository.get(key);

+ 11 - 3
xxl-job-admin/src/main/java/com/xxl/job/admin/service/impl/AdminBizImpl.java Целия файл

13
 import com.xxl.job.core.biz.model.RegistryParam;
13
 import com.xxl.job.core.biz.model.RegistryParam;
14
 import com.xxl.job.core.biz.model.ReturnT;
14
 import com.xxl.job.core.biz.model.ReturnT;
15
 import com.xxl.job.core.handler.IJobHandler;
15
 import com.xxl.job.core.handler.IJobHandler;
16
-import org.apache.commons.lang3.StringUtils;
17
 import org.slf4j.Logger;
16
 import org.slf4j.Logger;
18
 import org.slf4j.LoggerFactory;
17
 import org.slf4j.LoggerFactory;
19
 import org.springframework.stereotype.Service;
18
 import org.springframework.stereotype.Service;
63
         String callbackMsg = null;
62
         String callbackMsg = null;
64
         if (IJobHandler.SUCCESS.getCode() == handleCallbackParam.getExecuteResult().getCode()) {
63
         if (IJobHandler.SUCCESS.getCode() == handleCallbackParam.getExecuteResult().getCode()) {
65
             XxlJobInfo xxlJobInfo = xxlJobInfoDao.loadById(log.getJobId());
64
             XxlJobInfo xxlJobInfo = xxlJobInfoDao.loadById(log.getJobId());
66
-            if (xxlJobInfo!=null && StringUtils.isNotBlank(xxlJobInfo.getChildJobId())) {
65
+            if (xxlJobInfo!=null && xxlJobInfo.getChildJobId()!=null && xxlJobInfo.getChildJobId().trim().length()>0) {
67
                 callbackMsg = "<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>"+ I18nUtil.getString("jobconf_trigger_child_run") +"<<<<<<<<<<< </span><br>";
66
                 callbackMsg = "<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>"+ I18nUtil.getString("jobconf_trigger_child_run") +"<<<<<<<<<<< </span><br>";
68
 
67
 
69
                 String[] childJobIds = xxlJobInfo.getChildJobId().split(",");
68
                 String[] childJobIds = xxlJobInfo.getChildJobId().split(",");
70
                 for (int i = 0; i < childJobIds.length; i++) {
69
                 for (int i = 0; i < childJobIds.length; i++) {
71
-                    int childJobId = (StringUtils.isNotBlank(childJobIds[i]) && StringUtils.isNumeric(childJobIds[i]))?Integer.valueOf(childJobIds[i]):-1;
70
+                    int childJobId = (childJobIds[i]!=null && childJobIds[i].trim().length()>0 && isNumeric(childJobIds[i]))?Integer.valueOf(childJobIds[i]):-1;
72
                     if (childJobId > 0) {
71
                     if (childJobId > 0) {
73
 
72
 
74
                         JobTriggerPoolHelper.trigger(childJobId, TriggerTypeEnum.PARENT, -1, null, null);
73
                         JobTriggerPoolHelper.trigger(childJobId, TriggerTypeEnum.PARENT, -1, null, null);
113
         return ReturnT.SUCCESS;
112
         return ReturnT.SUCCESS;
114
     }
113
     }
115
 
114
 
115
+    private boolean isNumeric(String str){
116
+        try {
117
+            int result = Integer.valueOf(str);
118
+            return true;
119
+        } catch (NumberFormatException e) {
120
+            return false;
121
+        }
122
+    }
123
+
116
     @Override
124
     @Override
117
     public ReturnT<String> registry(RegistryParam registryParam) {
125
     public ReturnT<String> registry(RegistryParam registryParam) {
118
         int ret = xxlJobRegistryDao.registryUpdate(registryParam.getRegistGroup(), registryParam.getRegistryKey(), registryParam.getRegistryValue());
126
         int ret = xxlJobRegistryDao.registryUpdate(registryParam.getRegistGroup(), registryParam.getRegistryKey(), registryParam.getRegistryValue());

+ 38 - 17
xxl-job-admin/src/main/java/com/xxl/job/admin/service/impl/XxlJobServiceImpl.java Целия файл

13
 import com.xxl.job.core.biz.model.ReturnT;
13
 import com.xxl.job.core.biz.model.ReturnT;
14
 import com.xxl.job.core.enums.ExecutorBlockStrategyEnum;
14
 import com.xxl.job.core.enums.ExecutorBlockStrategyEnum;
15
 import com.xxl.job.core.glue.GlueTypeEnum;
15
 import com.xxl.job.core.glue.GlueTypeEnum;
16
-import org.apache.commons.lang3.StringUtils;
17
-import org.apache.commons.lang3.time.DateUtils;
18
-import org.apache.commons.lang3.time.FastDateFormat;
16
+import com.xxl.job.core.util.DateUtil;
19
 import org.quartz.CronExpression;
17
 import org.quartz.CronExpression;
20
 import org.quartz.SchedulerException;
18
 import org.quartz.SchedulerException;
21
 import org.slf4j.Logger;
19
 import org.slf4j.Logger;
75
 		if (!CronExpression.isValidExpression(jobInfo.getJobCron())) {
73
 		if (!CronExpression.isValidExpression(jobInfo.getJobCron())) {
76
 			return new ReturnT<String>(ReturnT.FAIL_CODE, I18nUtil.getString("jobinfo_field_cron_unvalid") );
74
 			return new ReturnT<String>(ReturnT.FAIL_CODE, I18nUtil.getString("jobinfo_field_cron_unvalid") );
77
 		}
75
 		}
78
-		if (StringUtils.isBlank(jobInfo.getJobDesc())) {
76
+		if (jobInfo.getJobDesc()==null || jobInfo.getJobDesc().trim().length()==0) {
79
 			return new ReturnT<String>(ReturnT.FAIL_CODE, (I18nUtil.getString("system_please_input")+I18nUtil.getString("jobinfo_field_jobdesc")) );
77
 			return new ReturnT<String>(ReturnT.FAIL_CODE, (I18nUtil.getString("system_please_input")+I18nUtil.getString("jobinfo_field_jobdesc")) );
80
 		}
78
 		}
81
-		if (StringUtils.isBlank(jobInfo.getAuthor())) {
79
+		if (jobInfo.getAuthor()==null || jobInfo.getAuthor().trim().length()==0) {
82
 			return new ReturnT<String>(ReturnT.FAIL_CODE, (I18nUtil.getString("system_please_input")+I18nUtil.getString("jobinfo_field_author")) );
80
 			return new ReturnT<String>(ReturnT.FAIL_CODE, (I18nUtil.getString("system_please_input")+I18nUtil.getString("jobinfo_field_author")) );
83
 		}
81
 		}
84
 		if (ExecutorRouteStrategyEnum.match(jobInfo.getExecutorRouteStrategy(), null) == null) {
82
 		if (ExecutorRouteStrategyEnum.match(jobInfo.getExecutorRouteStrategy(), null) == null) {
90
 		if (GlueTypeEnum.match(jobInfo.getGlueType()) == null) {
88
 		if (GlueTypeEnum.match(jobInfo.getGlueType()) == null) {
91
 			return new ReturnT<String>(ReturnT.FAIL_CODE, (I18nUtil.getString("jobinfo_field_gluetype")+I18nUtil.getString("system_unvalid")) );
89
 			return new ReturnT<String>(ReturnT.FAIL_CODE, (I18nUtil.getString("jobinfo_field_gluetype")+I18nUtil.getString("system_unvalid")) );
92
 		}
90
 		}
93
-		if (GlueTypeEnum.BEAN==GlueTypeEnum.match(jobInfo.getGlueType()) && StringUtils.isBlank(jobInfo.getExecutorHandler())) {
91
+		if (GlueTypeEnum.BEAN==GlueTypeEnum.match(jobInfo.getGlueType()) && (jobInfo.getExecutorHandler()==null || jobInfo.getExecutorHandler().trim().length()==0) ) {
94
 			return new ReturnT<String>(ReturnT.FAIL_CODE, (I18nUtil.getString("system_please_input")+"JobHandler") );
92
 			return new ReturnT<String>(ReturnT.FAIL_CODE, (I18nUtil.getString("system_please_input")+"JobHandler") );
95
 		}
93
 		}
96
 
94
 
100
 		}
98
 		}
101
 
99
 
102
 		// ChildJobId valid
100
 		// ChildJobId valid
103
-		if (StringUtils.isNotBlank(jobInfo.getChildJobId())) {
104
-			String[] childJobIds = StringUtils.split(jobInfo.getChildJobId(), ",");
101
+		if (jobInfo.getChildJobId()!=null && jobInfo.getChildJobId().trim().length()>0) {
102
+			String[] childJobIds = jobInfo.getChildJobId().split(",");
105
 			for (String childJobIdItem: childJobIds) {
103
 			for (String childJobIdItem: childJobIds) {
106
-				if (StringUtils.isNotBlank(childJobIdItem) && StringUtils.isNumeric(childJobIdItem)) {
104
+				if (childJobIdItem!=null && childJobIdItem.trim().length()>0 && isNumeric(childJobIdItem)) {
107
 					XxlJobInfo childJobInfo = xxlJobInfoDao.loadById(Integer.valueOf(childJobIdItem));
105
 					XxlJobInfo childJobInfo = xxlJobInfoDao.loadById(Integer.valueOf(childJobIdItem));
108
 					if (childJobInfo==null) {
106
 					if (childJobInfo==null) {
109
 						return new ReturnT<String>(ReturnT.FAIL_CODE,
107
 						return new ReturnT<String>(ReturnT.FAIL_CODE,
114
 							MessageFormat.format((I18nUtil.getString("jobinfo_field_childJobId")+"({0})"+I18nUtil.getString("system_unvalid")), childJobIdItem));
112
 							MessageFormat.format((I18nUtil.getString("jobinfo_field_childJobId")+"({0})"+I18nUtil.getString("system_unvalid")), childJobIdItem));
115
 				}
113
 				}
116
 			}
114
 			}
117
-			jobInfo.setChildJobId(StringUtils.join(childJobIds, ","));
115
+
116
+			String temp = "";	// join ,
117
+			for (String item:childJobIds) {
118
+				temp += item + ",";
119
+			}
120
+			temp = temp.substring(0, temp.length()-1);
121
+
122
+			jobInfo.setChildJobId(temp);
118
 		}
123
 		}
119
 
124
 
120
 		// add in db
125
 		// add in db
126
 		return new ReturnT<String>(String.valueOf(jobInfo.getId()));
131
 		return new ReturnT<String>(String.valueOf(jobInfo.getId()));
127
 	}
132
 	}
128
 
133
 
134
+	private boolean isNumeric(String str){
135
+		try {
136
+			int result = Integer.valueOf(str);
137
+			return true;
138
+		} catch (NumberFormatException e) {
139
+			return false;
140
+		}
141
+	}
142
+
129
 	@Override
143
 	@Override
130
 	public ReturnT<String> update(XxlJobInfo jobInfo) {
144
 	public ReturnT<String> update(XxlJobInfo jobInfo) {
131
 
145
 
133
 		if (!CronExpression.isValidExpression(jobInfo.getJobCron())) {
147
 		if (!CronExpression.isValidExpression(jobInfo.getJobCron())) {
134
 			return new ReturnT<String>(ReturnT.FAIL_CODE, I18nUtil.getString("jobinfo_field_cron_unvalid") );
148
 			return new ReturnT<String>(ReturnT.FAIL_CODE, I18nUtil.getString("jobinfo_field_cron_unvalid") );
135
 		}
149
 		}
136
-		if (StringUtils.isBlank(jobInfo.getJobDesc())) {
150
+		if (jobInfo.getJobDesc()==null || jobInfo.getJobDesc().trim().length()==0) {
137
 			return new ReturnT<String>(ReturnT.FAIL_CODE, (I18nUtil.getString("system_please_input")+I18nUtil.getString("jobinfo_field_jobdesc")) );
151
 			return new ReturnT<String>(ReturnT.FAIL_CODE, (I18nUtil.getString("system_please_input")+I18nUtil.getString("jobinfo_field_jobdesc")) );
138
 		}
152
 		}
139
-		if (StringUtils.isBlank(jobInfo.getAuthor())) {
153
+		if (jobInfo.getAuthor()==null || jobInfo.getAuthor().trim().length()==0) {
140
 			return new ReturnT<String>(ReturnT.FAIL_CODE, (I18nUtil.getString("system_please_input")+I18nUtil.getString("jobinfo_field_author")) );
154
 			return new ReturnT<String>(ReturnT.FAIL_CODE, (I18nUtil.getString("system_please_input")+I18nUtil.getString("jobinfo_field_author")) );
141
 		}
155
 		}
142
 		if (ExecutorRouteStrategyEnum.match(jobInfo.getExecutorRouteStrategy(), null) == null) {
156
 		if (ExecutorRouteStrategyEnum.match(jobInfo.getExecutorRouteStrategy(), null) == null) {
147
 		}
161
 		}
148
 
162
 
149
 		// ChildJobId valid
163
 		// ChildJobId valid
150
-		if (StringUtils.isNotBlank(jobInfo.getChildJobId())) {
151
-			String[] childJobIds = StringUtils.split(jobInfo.getChildJobId(), ",");
164
+		if (jobInfo.getChildJobId()!=null && jobInfo.getChildJobId().trim().length()>0) {
165
+			String[] childJobIds = jobInfo.getChildJobId().split(",");
152
 			for (String childJobIdItem: childJobIds) {
166
 			for (String childJobIdItem: childJobIds) {
153
-				if (StringUtils.isNotBlank(childJobIdItem) && StringUtils.isNumeric(childJobIdItem)) {
167
+				if (childJobIdItem!=null && childJobIdItem.trim().length()>0 && isNumeric(childJobIdItem)) {
154
 					XxlJobInfo childJobInfo = xxlJobInfoDao.loadById(Integer.valueOf(childJobIdItem));
168
 					XxlJobInfo childJobInfo = xxlJobInfoDao.loadById(Integer.valueOf(childJobIdItem));
155
 					if (childJobInfo==null) {
169
 					if (childJobInfo==null) {
156
 						return new ReturnT<String>(ReturnT.FAIL_CODE,
170
 						return new ReturnT<String>(ReturnT.FAIL_CODE,
161
 							MessageFormat.format((I18nUtil.getString("jobinfo_field_childJobId")+"({0})"+I18nUtil.getString("system_unvalid")), childJobIdItem));
175
 							MessageFormat.format((I18nUtil.getString("jobinfo_field_childJobId")+"({0})"+I18nUtil.getString("system_unvalid")), childJobIdItem));
162
 				}
176
 				}
163
 			}
177
 			}
164
-			jobInfo.setChildJobId(StringUtils.join(childJobIds, ","));
178
+
179
+			String temp = "";	// join ,
180
+			for (String item:childJobIds) {
181
+				temp += item + ",";
182
+			}
183
+			temp = temp.substring(0, temp.length()-1);
184
+
185
+			jobInfo.setChildJobId(temp);
165
 		}
186
 		}
166
 
187
 
167
 		// group valid
188
 		// group valid
348
 			}
369
 			}
349
 		} else {
370
 		} else {
350
             for (int i = 4; i > -1; i--) {
371
             for (int i = 4; i > -1; i--) {
351
-                triggerDayList.add(FastDateFormat.getInstance("yyyy-MM-dd").format(DateUtils.addDays(new Date(), -i)));
372
+                triggerDayList.add(DateUtil.formatDate(DateUtil.addDays(new Date(), -i)));
352
 				triggerDayCountRunningList.add(0);
373
 				triggerDayCountRunningList.add(0);
353
                 triggerDayCountSucList.add(0);
374
                 triggerDayCountSucList.add(0);
354
                 triggerDayCountFailList.add(0);
375
                 triggerDayCountFailList.add(0);

+ 1 - 2
xxl-job-admin/src/test/java/com/xxl/job/admin/dao/XxlJobLogDaoTest.java Целия файл

1
 package com.xxl.job.admin.dao;
1
 package com.xxl.job.admin.dao;
2
 
2
 
3
 import com.xxl.job.admin.core.model.XxlJobLog;
3
 import com.xxl.job.admin.core.model.XxlJobLog;
4
-import org.apache.commons.lang3.time.DateUtils;
5
 import org.junit.Test;
4
 import org.junit.Test;
6
 import org.junit.runner.RunWith;
5
 import org.junit.runner.RunWith;
7
 import org.springframework.test.context.ContextConfiguration;
6
 import org.springframework.test.context.ContextConfiguration;
48
         dto = xxlJobLogDao.load(log.getId());
47
         dto = xxlJobLogDao.load(log.getId());
49
 
48
 
50
 
49
 
51
-        List<Map<String, Object>> list2 = xxlJobLogDao.triggerCountByDay(DateUtils.addDays(new Date(), 30), new Date());
50
+        List<Map<String, Object>> list2 = xxlJobLogDao.triggerCountByDay(new Date(new Date().getTime() + 30*24*60*60*1000), new Date());
52
 
51
 
53
         int ret4 = xxlJobLogDao.clearLog(1, 1, new Date(), 100);
52
         int ret4 = xxlJobLogDao.clearLog(1, 1, new Date(), 100);
54
 
53
 

+ 1 - 1
xxl-job-core/src/main/java/com/xxl/job/core/log/XxlJobLogger.java Целия файл

30
         StackTraceElement callInfo = stackTraceElements[1];*/
30
         StackTraceElement callInfo = stackTraceElements[1];*/
31
 
31
 
32
         StringBuffer stringBuffer = new StringBuffer();
32
         StringBuffer stringBuffer = new StringBuffer();
33
-        stringBuffer.append(DateUtil.format(new Date())).append(" ")
33
+        stringBuffer.append(DateUtil.formatDateTime(new Date())).append(" ")
34
             .append("["+ callInfo.getClassName() + "#" + callInfo.getMethodName() +"]").append("-")
34
             .append("["+ callInfo.getClassName() + "#" + callInfo.getMethodName() +"]").append("-")
35
             .append("["+ callInfo.getLineNumber() +"]").append("-")
35
             .append("["+ callInfo.getLineNumber() +"]").append("-")
36
             .append("["+ Thread.currentThread().getName() +"]").append(" ")
36
             .append("["+ Thread.currentThread().getName() +"]").append(" ")

+ 129 - 11
xxl-job-core/src/main/java/com/xxl/job/core/util/DateUtil.java Целия файл

1
 package com.xxl.job.core.util;
1
 package com.xxl.job.core.util;
2
 
2
 
3
+import org.slf4j.Logger;
4
+import org.slf4j.LoggerFactory;
5
+
6
+import java.text.DateFormat;
3
 import java.text.ParseException;
7
 import java.text.ParseException;
4
 import java.text.SimpleDateFormat;
8
 import java.text.SimpleDateFormat;
9
+import java.util.Calendar;
5
 import java.util.Date;
10
 import java.util.Date;
11
+import java.util.HashMap;
12
+import java.util.Map;
6
 
13
 
7
 /**
14
 /**
8
  * date util
15
  * date util
10
  * @author xuxueli 2018-08-19 01:24:11
17
  * @author xuxueli 2018-08-19 01:24:11
11
  */
18
  */
12
 public class DateUtil {
19
 public class DateUtil {
13
-    private static final String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";
14
 
20
 
15
-    private static ThreadLocal<SimpleDateFormat> threadLocal = new ThreadLocal<SimpleDateFormat>() {
16
-        @Override
17
-        protected synchronized SimpleDateFormat initialValue() {
18
-            //return super.initialValue();
19
-            return new SimpleDateFormat(DATE_FORMAT);
21
+    // ---------------------- format parse ----------------------
22
+    private static Logger logger = LoggerFactory.getLogger(DateUtil.class);
23
+
24
+    private static final String DATE_FORMAT = "yyyy-MM-dd";
25
+    private static final String DATETIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
26
+
27
+    private static ThreadLocal<Map<String, DateFormat>> dateFormatThreadLocal = new ThreadLocal<Map<String, DateFormat>>();
28
+    private static DateFormat getDateFormat(String pattern) {
29
+        if (pattern==null || pattern.trim().length()==0) {
30
+            throw new IllegalArgumentException("pattern cannot be empty.");
31
+        }
32
+
33
+        Map<String, DateFormat> dateFormatMap = dateFormatThreadLocal.get();
34
+        if(dateFormatMap!=null && dateFormatMap.containsKey(pattern)){
35
+            return dateFormatMap.get(pattern);
36
+        }
37
+
38
+        synchronized (dateFormatThreadLocal) {
39
+            if (dateFormatMap == null) {
40
+                dateFormatMap = new HashMap<String, DateFormat>();
41
+            }
42
+            dateFormatMap.put(pattern, new SimpleDateFormat(pattern));
43
+            dateFormatThreadLocal.set(dateFormatMap);
44
+        }
45
+
46
+        return dateFormatMap.get(pattern);
47
+    }
48
+
49
+    /**
50
+     * format datetime. like "yyyy-MM-dd"
51
+     *
52
+     * @param date
53
+     * @return
54
+     * @throws ParseException
55
+     */
56
+    public static String formatDate(Date date) {
57
+        return format(date, DATE_FORMAT);
58
+    }
59
+
60
+    /**
61
+     * format date. like "yyyy-MM-dd HH:mm:ss"
62
+     *
63
+     * @param date
64
+     * @return
65
+     * @throws ParseException
66
+     */
67
+    public static String formatDateTime(Date date) {
68
+        return format(date, DATETIME_FORMAT);
69
+    }
70
+
71
+    /**
72
+     * format date
73
+     *
74
+     * @param date
75
+     * @param patten
76
+     * @return
77
+     * @throws ParseException
78
+     */
79
+    public static String format(Date date, String patten) {
80
+        return getDateFormat(patten).format(date);
81
+    }
82
+
83
+    /**
84
+     * parse date string, like "yyyy-MM-dd HH:mm:s"
85
+     *
86
+     * @param dateString
87
+     * @return
88
+     * @throws ParseException
89
+     */
90
+    public static Date parseDate(String dateString){
91
+        return parse(dateString, DATE_FORMAT);
92
+    }
93
+
94
+    /**
95
+     * parse datetime string, like "yyyy-MM-dd HH:mm:ss"
96
+     *
97
+     * @param dateString
98
+     * @return
99
+     * @throws ParseException
100
+     */
101
+    public static Date parseDateTime(String dateString) {
102
+        return parse(dateString, DATETIME_FORMAT);
103
+    }
104
+
105
+    /**
106
+     * parse date
107
+     *
108
+     * @param dateString
109
+     * @param pattern
110
+     * @return
111
+     * @throws ParseException
112
+     */
113
+    public static Date parse(String dateString, String pattern) {
114
+        try {
115
+            Date date = getDateFormat(pattern).parse(dateString);
116
+            return date;
117
+        } catch (Exception e) {
118
+            logger.warn("parse date error, dateString = {}, pattern={}; errorMsg = ", dateString, pattern, e.getMessage());
119
+            return null;
20
         }
120
         }
21
-    };
121
+    }
122
+
22
 
123
 
23
-    public static String format(Date date) {
24
-        return threadLocal.get().format(date);
124
+    // ---------------------- add date ----------------------
125
+
126
+    public static Date addDays(final Date date, final int amount) {
127
+        return add(date, Calendar.DAY_OF_MONTH, amount);
128
+    }
129
+
130
+    public static Date addYears(final Date date, final int amount) {
131
+        return add(date, Calendar.YEAR, amount);
25
     }
132
     }
26
 
133
 
27
-    public static Date parse(String textDate) throws ParseException {
28
-        return threadLocal.get().parse(textDate);
134
+    public static Date addMonths(final Date date, final int amount) {
135
+        return add(date, Calendar.MONTH, amount);
29
     }
136
     }
137
+
138
+    private static Date add(final Date date, final int calendarField, final int amount) {
139
+        if (date == null) {
140
+            return null;
141
+        }
142
+        final Calendar c = Calendar.getInstance();
143
+        c.setTime(date);
144
+        c.add(calendarField, amount);
145
+        return c.getTime();
146
+    }
147
+
30
 }
148
 }