[转载]Process工具类,提供设置timeout功能
FROM:http://segmentfault.com/blog/lidonghao/1190000000372535
在前一篇博文中,简单介绍了如何使用Process类来调用命令行的功能,那样使用Process会有一个很大的问题,就是可能会出现无限阻塞的情况,永远都无法返回结果。以下是Process的API说明,注意加粗的部分。
ProcessBuilder.start() 和 Runtime.exec 方法创建一个本机进程,并返回 Process 子类的一个实例,该实例可用来控制进程并获得相关信息。Process 类提供了执行从进程输入、执行输出到进程、等待进程完成、检查进程的退出状态以及销毁(杀掉)进程的方法。
创建进程的方法可能无法针对某些本机平台上的特定进程很好地工作,比如,本机窗口进程,守护进程,Microsoft Windows 上的 Win16/DOS 进程,或者 shell 脚本。创建的子进程没有自己的终端或控制台。它的所有标准 io(即 stdin、stdout 和 stderr)操作都将通过三个流 (getOutputStream()、getInputStream() 和 getErrorStream()) 重定向到父进程。父进程使用这些流来提供到子进程的输入和获得从子进程的输出。因为有些本机平台仅针对标准输入和输出流提供有限的缓冲区大小,如果读写子进程的输出流或输入流迅速出现失败,则可能导致子进程阻塞,甚至产生死锁。
解决进程无限阻塞的方法是在执行命令时,设置一个超时时间,下面提供一个工具类,对Process使用进行包装,向外提供设置超时的接口。
- ExecuteResult类,对执行命令的结果进行封装,可以从中获取退出码和输出内容。
public class ExecuteResult {
@Override
public String toString() {
return "ExecuteResult [exitCode=" + exitCode + ", executeOut="
+ executeOut + "]";
} private int exitCode;
private String executeOut; public ExecuteResult(int exitCode, String executeOut) {
super();
this.exitCode = exitCode;
this.executeOut = executeOut;
} public int getExitCode() {
return exitCode;
} public void setExitCode(int exitCode) {
this.exitCode = exitCode;
} public String getExecuteOut() {
return executeOut;
} public void setExecuteOut(String executeOut) {
this.executeOut = executeOut;
} }- LocalCommandExecutorService 接口,向外暴露executeCommand()方法
public interface LocalCommandExecutorService {
ExecuteResult executeCommand(String[] command, long timeout);
}- LocalCommandExecutorServiceImpl 实现类,实现LocalCommandExecutorService 接口的方法
public class LocalCommandExecutorServiceImpl implements
LocalCommandExecutorService {
static final Logger logger = LoggerFactory
.getLogger(LocalCommandExecutorServiceImpl.class); static ExecutorService pool = new ThreadPoolExecutor(0, Integer.MAX_VALUE,
3L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>()); @Override
public ExecuteResult executeCommand(String[] command, long timeout) {
Process process = null;
InputStream pIn = null;
InputStream pErr = null;
StreamGobbler outputGobbler = null;
StreamGobbler errorGobbler = null;
Future<Integer> executeFuture = null;
try {
process = Runtime.getRuntime().exec(command);
final Process p = process; //close process's output stream.
p.getOutputStream().close(); pIn = process.getInputStream();
outputGobbler = new StreamGobbler(
pIn, "OUTPUT");
outputGobbler.start(); pErr = process.getErrorStream();
errorGobbler = new StreamGobbler(pErr, "ERROR");
errorGobbler.start(); // create a Callable for the command's Process which can be called
// by an Executor
Callable<Integer> call = new Callable<Integer>() {
public Integer call() throws Exception {
p.waitFor();
return p.exitValue();
}
}; // submit the command's call and get the result from a
executeFuture = pool.submit(call);
int exitCode = executeFuture.get(timeout,
TimeUnit.MILLISECONDS);
return new ExecuteResult(exitCode, outputGobbler.getContent());
} catch (IOException ex) {
String errorMessage = "The command [" + command
+ "] execute failed.";
logger.error(errorMessage, ex);
return new ExecuteResult(-1, null);
} catch (TimeoutException ex) {
String errorMessage = "The command [" + command + "] timed out.";
logger.error(errorMessage, ex);
return new ExecuteResult(-1, null);
} catch (ExecutionException ex) {
String errorMessage = "The command [" + command
+ "] did not complete due to an execution error.";
logger.error(errorMessage, ex);
return new ExecuteResult(-1, null);
} catch (InterruptedException ex) {
String errorMessage = "The command [" + command
+ "] did not complete due to an interrupted error.";
logger.error(errorMessage, ex);
return new ExecuteResult(-1, null);
} finally {
if(executeFuture != null){
try{
executeFuture.cancel(true);
} catch(Exception ignore){}
}
if(pIn != null) {
this.closeQuietly(pIn);
if(outputGobbler != null && !outputGobbler.isInterrupted()){
outputGobbler.interrupt();
}
}
if(pErr != null) {
this.closeQuietly(pErr);
if(errorGobbler != null && !errorGobbler.isInterrupted()){
errorGobbler.interrupt();
}
}
if (process != null) {
process.destroy();
}
}
} private void closeQuietly(Closeable c) {
try {
if (c != null)
c.close();
} catch (IOException e) {
}
}
}- StreamGobbler类,用来包装输入输出流
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; public class StreamGobbler extends Thread {
private static Logger logger = LoggerFactory.getLogger(StreamGobbler.class);
private InputStream inputStream;
private String streamType;
private StringBuilder buf;
private volatile boolean isStopped = false; /**
* Constructor.
*
* @param inputStream
* the InputStream to be consumed
* @param streamType
* the stream type (should be OUTPUT or ERROR)
* @param displayStreamOutput
* whether or not to display the output of the stream being
* consumed
*/
public StreamGobbler(final InputStream inputStream, final String streamType) {
this.inputStream = inputStream;
this.streamType = streamType;
this.buf = new StringBuilder();
this.isStopped = false;
} /**
* Consumes the output from the input stream and displays the lines
* consumed if configured to do so.
*/
@Override
public void run() {
try {
//默认编码为UTF-8,这里设置编码为GBK,因为WIN7的编码为GBK
InputStreamReader inputStreamReader = new InputStreamReader(
inputStream,"GBK");
BufferedReader bufferedReader = new BufferedReader(
inputStreamReader);
String line = null;
while ((line = bufferedReader.readLine()) != null) {
this.buf.append(line + "\n");
}
} catch (IOException ex) {
logger.trace("Failed to successfully consume and display the input stream of type "
+ streamType + ".", ex);
} finally {
this.isStopped = true;
synchronized (this) {
notify();
}
}
} public String getContent() {
if(!this.isStopped){
synchronized (this) {
try {
wait();
} catch (InterruptedException ignore) {
}
}
}
return this.buf.toString();
}
}- 测试用例
public class LocalCommandExecutorTest {
public static void main(String[] args) {
LocalCommandExecutorService service = new LocalCommandExecutorServiceImpl();
String[] command = new String[]{"ping","127.0.0.1"};
ExecuteResult result = service.executeCommand(command, 5000);
System.out.println("退出码:"+result.getExitCode());
System.out.println("输出内容:"+result.getExecuteOut());
}
}输出结果如下:
直接在命令行执行“ping 127.0.0.1”,结果如下:
Apache提供了一个开源库,对Process类进行了封装,也提供了设置超时的功能,建议在项目中使用Apache Commons Exec这个开源库来实现超时功能,除了功能更强大外,稳定性也有保障。
[转载]Process工具类,提供设置timeout功能的更多相关文章
- 反射工具类.提供调用getter/setter方法, 访问私有变量, 调用私有方法, 获取泛型类型Class,被AOP过的真实类等工具函数.java
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.Validate; import org.ap ...
- 由于Java的简单类型不能够精确的对浮点数进行运算,这个工具类提供精 确的浮点数运算,包括加减乘除和四舍五入。
package com.minxinloan.utils; import java.math.BigDecimal; public class Arith { // 源文件Arith.java: /* ...
- jackson工具类有动态属性过虑功能
在业务应用中经常会有指定属性序列化json的需求,C#中这个功能很容易就可以解决:使用lambda重新构造一下匿名对象就可以了.一行代码搞定.java是这样解决的. public JsonMapper ...
- 用Java开发一个工具类,提供似于js中eval函数功能的eval方法
今天在看到<Java疯狂讲义>中一个章节习题: 开发一个工具类,该工具类提供一个eval()方法,实现JavaScript中eval()函数的功能--可以动态运行一行或多行程序代码.例如: ...
- C#工具类:Json操作帮助类(转载)
原文转载自C#工具类:Json操作帮助类_IT技术小趣屋. Json序列化和反序列化在程序开发中时常会遇到,在C#中可以使用很多种方法实现对数据的Json序列化和反序列化,封装一个Json操作工具类来 ...
- 【转载】C#工具类:Json操作帮助类
Json序列化和反序列化在程序开发中时常会遇到,在C#中可以使用很多种方法实现对数据的Json序列化和反序列化,封装一个Json操作工具类来简化相应的操作,该工具类中包含以下功能:对象转JSON.数据 ...
- 并发工具类(一)等待多线程的CountDownLatch
前言 JDK中为了处理线程之间的同步问题,除了提供锁机制之外,还提供了几个非常有用的并发工具类:CountDownLatch.CyclicBarrier.Semphore.Exchanger.Ph ...
- Spark中经常使用工具类Utils的简明介绍
<深入理解Spark:核心思想与源代码分析>一书前言的内容请看链接<深入理解SPARK:核心思想与源代码分析>一书正式出版上市 <深入理解Spark:核心思想与源代码分析 ...
- Spark中常用工具类Utils的简明介绍
<深入理解Spark:核心思想与源码分析>一书前言的内容请看链接<深入理解SPARK:核心思想与源码分析>一书正式出版上市 <深入理解Spark:核心思想与源码分析> ...
随机推荐
- P1613 跑路 (最短路,倍增)
题目链接 Solution 发现 \(n\) 只有 \(50\), 可以用 \(floyd\) . 然后 \(w[i][j][l]\) 代表 \(i\) 到 \(j\) 是否存在 \(2^l\) 长的 ...
- ftp无法连接的原因
1.需求 记录碰到的ftp无法连接的原因 2.解决方案 .确认ftp服务开启. .确认21端口没有被占用. .确认有目录的执行权限. .确认配置文件中的目录读写权限正确. .关闭SELinux 修改/ ...
- Angular中checkbox实现复选
需求:实现点击子选项,父选项自动勾选,当子选项没有勾选,对应的父选项不勾选,并把勾选的对应的id发送出去. 效果图: <!DOCTYPE html> <html data-ng-ap ...
- 洛谷T8116 密码
T8116 密码 题目描述 YJC把核弹发射密码忘掉了……其实是密码被加密了,但是YJC不会解密.密码由n个数字组成,第i个数字被加密成了如下形式:第k小的满足(2^L)|(P-1)且P为质数的P.Y ...
- 转 linux下cat命令详解
linux下cat命令详解 http://www.cnblogs.com/perfy/archive/2012/07/23/2605550.html 简略版: cat主要有三大功能:1.一次显示整个文 ...
- 使用 WideCharToMultiByte Unicode 与 UTF-8互转
1.简述 最近在发送网络请求时遇到了中文字符乱码的问题,在代码中调试字符正常,用抓包工具抓的包中文字符显示正常,就是发送到服务器就显示乱码了,那就要将客户端和服务器设置统一的编码(UTF-8),而我们 ...
- java的io操作(将字符串写入到txt文件中)
import java.io.File;import java.io.FileNotFoundException;import java.io.FileOutputStream;import java ...
- hdu 5086(递推)
Revenge of Segment Tree Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/ ...
- 【原创】CMD命令设置IP地址
问题描述 在实际工作中,尤其是像我们这种BI分析人员,在做项目的时候,时常都需要因客户的不同随时切换不同的网络环境,有时可能需要在公司和客户之间来回的穿梭.交替.问题也就随之而来:每次客户那里都需要设 ...
- FTP-Filezilla首次配置
最新新弄了个服务器,先吐槽下,之前买镜像都是免费的,昨天试了,竟然收费.... 好吧,用户多了也正常. 代码发布之前都是很暴力的直接远程桌面然后粘贴,有个合作伙伴突然需要FTP,说之前用的就是,我就做 ...