FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

基础类库重构 · sherlock111/java-code@bc89242 · GitHub

Repository navigation

Commit bc89242

Browse files
committed
基础类库重构
1 parent bcb9705 commit bc89242

22 files changed

Lines changed: 585 additions & 312 deletions

File tree

‎basicKnowledge/src/com/xdc/basic/api/apache/commons/exec/ExecTest.java‎

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,15 +78,18 @@ private static DefaultExecutor getExecutor()
7878

7979
// 设置超时时间,超时后,ExecuteWatchdog.timeoutOccured(Watchdog w)调用Process.destroy()杀死命令行进程,类似于kill命令,但是此时命令行进程的返回值是不可信的,就是说无法确认是程序正常执行完毕,返回了一个值,还是因为超时返回了一个值。为了区分是否超时,可以配合使用DefaultExecuteResultHandler.waitFor(long timeout)进行区分,具体操作是:假设外界要求的超时时间是60秒,那么watchdog设置成60+10秒,waitFor参数设置成60秒,这样就能做到可以区分是否超时,并在超时后杀死命令行进程。
8080
// 通过测试发现:
81-
// (1)在window上:命令行进程被Process.destroy()杀死,返回值为1;在任务管理器被结束进程杀死,返回值为1。
81+
// (1)在windows上:命令行进程被Process.destroy()杀死,返回值为1;在任务管理器被结束进程杀死,返回值为1。
8282
// (2)在linux(suse)上:命令行进程被Process.destroy()杀死,返回值为143;在任务管理器被结束进程杀死,返回值为137。
8383
ExecuteWatchdog watchdog = new ExecuteWatchdog(20 * 1000L);
8484
executor.setWatchdog(watchdog);
8585

8686
// 设置期望返回值,不符合抛异常ExecuteException。这里置为空,禁用此功能,由业务自己处理返回值,无需业务捕获异常。
8787
executor.setExitValues(null);
8888

89-
// 设置关闭钩子,当jvm退出时,杀死正在执行的命令行进程
89+
// 设置关闭钩子,当jvm退出时,杀死正在执行的命令行进程。
90+
// 在不设置关闭钩子的情况下,通过测试发现:
91+
// (1)windows进程退出特点(使用procexp.exe观察):父进程退出后,子进程还是在运行的,并且子进程的父进程id无变化。(感觉有些不合理,父进程id已经不存在了,不知道windows为什么这么设计)
92+
// (2)linux进程退出特点(使用ps观察):父进程退出后,子进程还是在运行的,但是子进程的父进程id会变成1,也就是1号进程。
9093
executor.setProcessDestroyer(new ShutdownHookProcessDestroyer());
9194

9295
return executor;
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
package com.xdc.basic.api.apache.commons.exec.framwaork;
2+
3+
import java.io.File;
4+
import java.util.Map;
5+
6+
import org.apache.commons.lang3.builder.ToStringBuilder;
7+
import org.apache.commons.lang3.builder.ToStringStyle;
8+
9+
public class ExecCommand
10+
{
11+
/**
12+
* 执行的命令,支持管道和重定向。
13+
*/
14+
private String command;
15+
16+
/**
17+
* 命令行调用的标准输入流。比如删除文件要确认,测试命令:touch xdc.log && rm -i xdc.log
18+
*/
19+
private String stdIn = null;
20+
21+
/**
22+
* 设置执行目录。一般是不需要的,有些场景需要切换到其他目录执行目录。
23+
*/
24+
private File workingDirectory = new File(".");
25+
26+
/**
27+
* 可以定制环境变量。留空的话,默认继承java程序的环境变量。
28+
*/
29+
private Map<Object, Object> environment = null;
30+
31+
/**
32+
* 超时时间,单位毫秒。超时后,再等待10秒钟,杀死命令行进程,否则会残留,浪费资源。等10秒的原因是为了区分正常返回和执行超时。
33+
* 因为杀死进程后,进程也是有返回值的,看起来与执行完成没有区别。虽然执行成功(惯例是0)、失败、超时被杀死的返回码不同,也是不能根据返回值判断是否超时的,那样代码就跟操作系统的耦合了。
34+
*/
35+
private long timeout = 60 * 1000L;
36+
37+
/**
38+
* 期待的返回值列表,只有返回值包含在此列表中,才认为此次调用成功。根据惯例,值0表示正常终止。此处提供定制能力,以适应一些设计古怪的可执行命令。
39+
*/
40+
private int[] expectedExitValues = new int[] { 0 };
41+
42+
public ExecCommand(String command)
43+
{
44+
super();
45+
this.command = command;
46+
}
47+
48+
public String getCommand()
49+
{
50+
return command;
51+
}
52+
53+
public void setCommand(String command)
54+
{
55+
this.command = command;
56+
}
57+
58+
public String getStdIn()
59+
{
60+
return stdIn;
61+
}
62+
63+
public void setStdIn(String stdIn)
64+
{
65+
this.stdIn = stdIn;
66+
}
67+
68+
public File getWorkingDirectory()
69+
{
70+
return workingDirectory;
71+
}
72+
73+
public void setWorkingDirectory(File workingDirectory)
74+
{
75+
this.workingDirectory = workingDirectory;
76+
}
77+
78+
public Map<Object, Object> getEnvironment()
79+
{
80+
return environment;
81+
}
82+
83+
public void setEnvironment(Map<Object, Object> environment)
84+
{
85+
this.environment = environment;
86+
}
87+
88+
public int[] getExpectedExitValues()
89+
{
90+
return expectedExitValues;
91+
}
92+
93+
public void setExpectedExitValues(int[] expectedExitValues)
94+
{
95+
this.expectedExitValues = (expectedExitValues == null ? null : (int[]) expectedExitValues.clone());
96+
}
97+
98+
public void setExpectedExitValue(int expectedExitValue)
99+
{
100+
this.setExpectedExitValues(new int[] { expectedExitValue });
101+
}
102+
103+
public long getTimeout()
104+
{
105+
return timeout;
106+
}
107+
108+
public void setTimeout(long timeout)
109+
{
110+
if (timeout < 10 * 1000)
111+
{
112+
throw new IllegalArgumentException(
113+
String.format("Timeout[%s] is not vaild. Timeout must be greater than 10000.", timeout));
114+
}
115+
116+
this.timeout = timeout;
117+
}
118+
119+
@Override
120+
public String toString()
121+
{
122+
return ToStringBuilder.reflectionToString(this, ToStringStyle.MULTI_LINE_STYLE);
123+
}
124+
}
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
package com.xdc.basic.api.apache.commons.exec.framwaork;
2+
3+
import org.apache.commons.lang3.builder.ToStringBuilder;
4+
import org.apache.commons.lang3.builder.ToStringStyle;
5+
6+
public class ExecResult
7+
{
8+
/**
9+
* 命令行调用框架异常,出现此返回值,需要考虑修改框架缺陷。
10+
*/
11+
public static final int EXEC_FAILURE = -1;
12+
13+
/**
14+
* 命令行调用超时,出现此返回值,需要排查脚本和可执行命令中是否有死循环等错误。
15+
*/
16+
public static final int EXEC_TIMEOUT = -2;
17+
18+
/**
19+
* 期待的返回值列表,只有返回值包含在此列表中,才认为此次调用成功。根据惯例,值0表示正常终止。此处提供定制能力,以适应一些设计古怪的可执行命令。
20+
*/
21+
private int[] expectedExitValues = new int[] { 0 };
22+
23+
/**
24+
* 命令行调用的返回值
25+
*/
26+
private int exitValue;
27+
28+
/**
29+
* 命令行调用的标准输出流
30+
*/
31+
private String stdOut;
32+
33+
/**
34+
* 命令行调用的标准错误流
35+
*/
36+
private String stdErr;
37+
38+
/**
39+
* 命令行调用框架异常。
40+
*/
41+
private Throwable throwable;
42+
43+
public ExecResult(int[] expectedExitValues, int exitValue, String stdOut, String stdErr)
44+
{
45+
super();
46+
this.expectedExitValues = expectedExitValues;
47+
this.exitValue = exitValue;
48+
this.stdOut = stdOut;
49+
this.stdErr = stdErr;
50+
}
51+
52+
public int[] getExpectedExitValues()
53+
{
54+
return expectedExitValues;
55+
}
56+
57+
public void setExpectedExitValues(int[] expectedExitValues)
58+
{
59+
this.expectedExitValues = expectedExitValues;
60+
}
61+
62+
public int getExitValue()
63+
{
64+
return exitValue;
65+
}
66+
67+
public void setExitValue(int exitValue)
68+
{
69+
this.exitValue = exitValue;
70+
}
71+
72+
public String getStdOut()
73+
{
74+
return stdOut;
75+
}
76+
77+
public void setStdOut(String stdOut)
78+
{
79+
this.stdOut = stdOut;
80+
}
81+
82+
public String getStdErr()
83+
{
84+
return stdErr;
85+
}
86+
87+
public void setStdErr(String stdErr)
88+
{
89+
this.stdErr = stdErr;
90+
}
91+
92+
public Throwable getThrowable()
93+
{
94+
return throwable;
95+
}
96+
97+
public void setThrowable(Throwable throwable)
98+
{
99+
this.throwable = throwable;
100+
}
101+
102+
@Override
103+
public String toString()
104+
{
105+
return ToStringBuilder.reflectionToString(this, ToStringStyle.MULTI_LINE_STYLE);
106+
}
107+
108+
public boolean isSuccess()
109+
{
110+
if (exitValue == EXEC_FAILURE || exitValue == EXEC_TIMEOUT)
111+
{
112+
return false;
113+
}
114+
115+
if (throwable != null)
116+
{
117+
return false;
118+
}
119+
120+
if (expectedExitValues == null || expectedExitValues.length == 0)
121+
{
122+
return true;
123+
}
124+
125+
for (int i = 0; i < expectedExitValues.length; i++)
126+
{
127+
if (expectedExitValues[i] == exitValue)
128+
{
129+
return true;
130+
}
131+
}
132+
133+
return false;
134+
}
135+
136+
public boolean isFailure(final int exitValue)
137+
{
138+
return !isSuccess();
139+
}
140+
}
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
package com.xdc.basic.api.apache.commons.exec.framwaork;
2+
3+
import java.io.ByteArrayInputStream;
4+
import java.io.ByteArrayOutputStream;
5+
6+
import org.apache.commons.exec.CommandLine;
7+
import org.apache.commons.exec.DefaultExecuteResultHandler;
8+
import org.apache.commons.exec.DefaultExecutor;
9+
import org.apache.commons.exec.ExecuteWatchdog;
10+
import org.apache.commons.exec.PumpStreamHandler;
11+
import org.apache.commons.exec.ShutdownHookProcessDestroyer;
12+
import org.apache.commons.lang3.StringUtils;
13+
import org.apache.commons.lang3.SystemUtils;
14+
15+
import com.xdc.basic.commons.codec.BytesUtil;
16+
17+
public class ExecTool
18+
{
19+
// 获取命令行的编码:
20+
// (1)windows下获取cmd.exe的编码,打开命令行后能在属性里看到的。
21+
// (2)linux下获取当前shell(如bash)的编码,执行locale可以查看。
22+
private static final String CMD_ENCODING = System.getProperty("sun.jnu.encoding");
23+
24+
public static ExecResult exec(ExecCommand execCommand)
25+
{
26+
// 创建执行命令行
27+
CommandLine commandLine = null;
28+
if (SystemUtils.IS_OS_UNIX)
29+
{
30+
commandLine = CommandLine.parse("sh");
31+
commandLine.addArguments(new String[] { "-c", execCommand.getCommand() }, false);
32+
}
33+
else if (SystemUtils.IS_OS_WINDOWS)
34+
{
35+
commandLine = CommandLine.parse("cmd");
36+
commandLine.addArguments(new String[] { "/c", execCommand.getCommand() }, false);
37+
}
38+
else
39+
{
40+
throw new RuntimeException("Unsupport OS type. Suppurt OS type:[UNIX-like, Windows].");
41+
}
42+
43+
// 创建执执行器
44+
DefaultExecutor executor = new DefaultExecutor();
45+
46+
// 设置标准输出流,标准错误留,标准输入流。值得注意的是ByteArrayInputStream和ByteArrayOutputStream是不用关闭的,因为不是它们不是真正的IO流,未与设备交互,不过是Java内部缓冲用的。其实查看他的的close方法,是空实现的。
47+
ByteArrayOutputStream stdOut = new ByteArrayOutputStream();
48+
ByteArrayOutputStream stdErr = new ByteArrayOutputStream();
49+
ByteArrayInputStream stdIn = null;
50+
if (StringUtils.isNotEmpty(execCommand.getStdIn()))
51+
{
52+
stdIn = new ByteArrayInputStream(BytesUtil.string2Bytes(execCommand.getStdIn(), CMD_ENCODING));
53+
}
54+
executor.setStreamHandler(new PumpStreamHandler(stdOut, stdErr, stdIn));
55+
56+
// 设置看门狗,超时后,再等待10秒钟,杀死命令行进程,否则会残留,浪费资源。等10秒的原因是为了区分正常返回和执行超时。
57+
// 因为杀死进程后,进程也是有返回值的,看起来与执行完成没有区别。虽然执行成功(惯例是0)、失败、超时被杀死的返回码不同,也是不能根据返回值判断是否超时的,那样代码就跟操作系统的耦合了。
58+
ExecuteWatchdog watchdog = new ExecuteWatchdog(execCommand.getTimeout() + 10 * 1000L);
59+
executor.setWatchdog(watchdog);
60+
61+
// 设置执行目录
62+
executor.setWorkingDirectory(execCommand.getWorkingDirectory());
63+
64+
// 设置期望返回值,不符合抛异常ExecuteException。这里置为空,禁用此功能,由业务自己处理返回值,无需业务捕获异常。
65+
executor.setExitValues(null);
66+
67+
// 设置关闭钩子,当jvm退出时,杀死正在执行的命令行进程。
68+
executor.setProcessDestroyer(new ShutdownHookProcessDestroyer());
69+
70+
// 创建执行结果处理器
71+
DefaultExecuteResultHandler defaultExecuteResultHandler = new DefaultExecuteResultHandler();
72+
73+
try
74+
{
75+
// 执行命令
76+
executor.execute(commandLine, execCommand.getEnvironment(), defaultExecuteResultHandler);
77+
78+
// 同步等待命令行进程完成
79+
defaultExecuteResultHandler.waitFor(execCommand.getTimeout());
80+
}
81+
catch (Throwable e)
82+
{
83+
// 执行失败,获取异常
84+
ExecResult execResult = new ExecResult(execCommand.getExpectedExitValues(), ExecResult.EXEC_FAILURE,
85+
BytesUtil.bytes2String(stdOut.toByteArray(), CMD_ENCODING),
86+
BytesUtil.bytes2String(stdErr.toByteArray(), CMD_ENCODING));
87+
execResult.setThrowable(e);
88+
89+
return execResult;
90+
}
91+
92+
if (defaultExecuteResultHandler.hasResult())
93+
{
94+
// 执行完成,获取返回值
95+
ExecResult execResult = new ExecResult(execCommand.getExpectedExitValues(),
96+
defaultExecuteResultHandler.getExitValue(),
97+
BytesUtil.bytes2String(stdOut.toByteArray(), CMD_ENCODING),
98+
BytesUtil.bytes2String(stdErr.toByteArray(), CMD_ENCODING));
99+
100+
execResult.setThrowable(defaultExecuteResultHandler.getException());
101+
102+
return execResult;
103+
}
104+
else
105+
{
106+
// 执行超时,返回值设置为空
107+
return new ExecResult(execCommand.getExpectedExitValues(), ExecResult.EXEC_TIMEOUT,
108+
BytesUtil.bytes2String(stdOut.toByteArray(), CMD_ENCODING),
109+
BytesUtil.bytes2String(stdErr.toByteArray(), CMD_ENCODING));
110+
}
111+
}
112+
}

0 commit comments

Comments
 (0)

Back | FazBrowse Home | New Git URL