最近在写一个Java工具,其中调用了各种SHELL命令,使用了Runtime.getRuntime().exec(command);这个方法。但是在我调用某个命令执行操作时,程序就卡在那了,但是其他操作却能够正常输出,经过了一番查找和摸索,终于明白了原来Java在执行命令时输出到某个Buffer里,这个Buffer是有容量限制的,如果满了一直没有人读取,就会一直等待,造成进程锁死的现象,知道这一点,我们就可以在执行的过程中新开启几个线程来不断地读取标准输出,以及错误输出:
 
final Process p = Runtime.getRuntime().exec(command);
new Thread(new Runnable() {
@Override
public void run() {
while (true){
try {
p.exitValue();
break;
} catch (Exception e){
showInfo(System.err,p.getErrorStream());
}
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
while (true){
try {
p.exitValue();
break;
} catch (Exception e){
showInfo(System.out,p.getInputStream());
}
}
}
}).start();
int exitValue = p.waitFor();
 
需要注意的是,在waitFor执行完成之前,错误输出和标准要分开处理。
 
Apache commons-exec提供一些常用的方法用来执行外部进程,Apache commons exec库提供了监视狗Watchdog来设监视进程的执行超时,同时也还实现了同步和异步功能,Apache commonsexec涉及到多线程,比如新启动一个进程,Java中需要再开三个线程来处理进程的三个数据流,分别是标准输入,标准输出和错误输出。
 
需要使用该功能需要引入commons-exec-1.3.jar包,目前最新的版本为1.3版本。
 
Apache commons-exec的官方网站:http://commons.apache.org/proper/commons-exec/
 
其中就有相应的示例Example,来详解如何使用该工具来执行shell命令,我们执行命令的相关代码:
 
final Long executeSubTaskId = subTaskExecuteContext.getSubTaskExecuteId();
final Long taskStatusId = subTaskExecuteContext.getTaskExecuteContext().getTaskExecuteId(); ByteArrayOutputStream outputStream =
new MzByteArrayOutputStream(executeSubTaskId, taskStatusNotifyCenter, true);
ByteArrayOutputStream errorStream =
new MzByteArrayOutputStream(executeSubTaskId, taskStatusNotifyCenter, false);
PumpStreamHandler streamHandler = new PumpStreamHandler(outputStream, errorStream);
taskThreadPoolExecutor.setStreamHandler(streamHandler); CommandLine commandLine = new CommandLine(new File(executeShellPath)); final TaskProcessInfo taskProcessInfo = new TaskProcessInfo(subTaskExecuteContext.getTaskExecuteContext().getTaskId(),
taskStatusId,
executeSubTaskId);
ProcessManagerDestroyer processManagerDestroyer =
new ProcessManagerDestroyer(
taskProcessInfo, processInfoManager);
taskThreadPoolExecutor.setProcessDestroyer(processManagerDestroyer);
try {
taskThreadPoolExecutor.execute(commandLine, new DefaultExecuteResultHandler() {
@Override
public void onProcessComplete(int exitValue) {
super.onProcessComplete(exitValue);
LOG.info(String.format("Task Process info: %s succeed!", taskProcessInfo));
taskStatusNotifyCenter.notifyEventChanged(new TaskStatusEvent(TaskStatusEventType.TASK_FINISHED,
new TaskEventObject(subTaskExecuteContext.getTaskExecuteContext().getTaskId(),
subTaskExecuteContext.getTaskExecuteContext().getTaskExecuteId(),
subTaskExecuteContext.getSubTaskExecuteId())));
} @Override
public void onProcessFailed(ExecuteException e) {
super.onProcessFailed(e);
LOG.error(e);
LOG.error(String.format("Task Process info: %s failed!", taskProcessInfo));
taskStatusNotifyCenter.notifyEventChanged(new TaskStatusEvent(TaskStatusEventType.TASK_FAILED,
new TaskEventObject(subTaskExecuteContext.getTaskExecuteContext().getTaskId(),
subTaskExecuteContext.getTaskExecuteContext().getTaskExecuteId(),
subTaskExecuteContext.getSubTaskExecuteId())));
}
});
} catch (IOException e) {
throw new BusinessException(e);
}
 
 
新建执行Process需要new两个ByteArrayOutputStream,一个用来记录标准输出流,一个用来记录错误输出流。为了及时清理ByteArrayOutputStream中的内容,可以选择性地将该输出流重写:
@Override
public synchronized void write(byte[] b, int off, int len) {
super.write(b, off, len);
writeTimes++;
writeLength += len;
if (writeLength >= MAX_WRITE_LENGTH || writeTimes >= MAX_WRITE_TIMES) {
updateStatus();
this.buf = new byte[32];
writeLength = 0;
writeTimes = 0;
} } @Override
public void flush() throws IOException {
super.flush();
updateStatus();
}
 
建立的ProcessManagerDestroyer用来任务创建或任务完成时,对任务的当前记录状态。
public class ProcessManagerDestroyer implements ProcessDestroyer {

    private final ProcessInfoManager processInfoManager;

    private final TaskProcessInfo taskProcessInfo;

    public ProcessManagerDestroyer(TaskProcessInfo taskProcessInfo, ProcessInfoManager processInfoManager) {
this.taskProcessInfo = taskProcessInfo;
this.processInfoManager = processInfoManager;
} @Override
public boolean add(Process process) {
processInfoManager.addProcess(taskProcessInfo, process);
return true;
} @Override
public boolean remove(Process process) {
processInfoManager.removeProcess(taskProcessInfo);
return true;
} @Override
public int size() {
return processInfoManager.taskCount();
}
 
在Destroyer中新建的Process可以保存,并在以后调用destroy方法将其kill掉:
 
process.destroy();
 
最后建立的DefaultExecuteResultHandler监听器用来在任务执行完成或出现错误时,提示对应的信息,并发送事件。
 
 
执行shell时遇到的问题,初步看来,没有执行的权限?
 
org.apache.commons.exec.ExecuteException: Execution failed (Exit value: -559038737. Caused by java.io.IOException: Cannot run program "/Users/mazhiqiang/Downloads/1.sh" (in directory "."): error=13, Permission denied)
at org.apache.commons.exec.DefaultExecutor$1.run(DefaultExecutor.java:205)
at java.lang.Thread.run(Thread.java:745)
Caused by: java.io.IOException: Cannot run program "/Users/xxx/Downloads/1.sh" (in directory "."): error=13, Permission denied
at java.lang.ProcessBuilder.start(ProcessBuilder.java:1042)
at java.lang.Runtime.exec(Runtime.java:620)
at org.apache.commons.exec.launcher.Java13CommandLauncher.exec(Java13CommandLauncher.java:61)
at org.apache.commons.exec.DefaultExecutor.launch(DefaultExecutor.java:279)
at org.apache.commons.exec.DefaultExecutor.executeInternal(DefaultExecutor.java:336)
at org.apache.commons.exec.DefaultExecutor.access$200(DefaultExecutor.java:48)
at org.apache.commons.exec.DefaultExecutor$1.run(DefaultExecutor.java:200)
... 1 more
Caused by: java.io.IOException: error=13, Permission denied
at java.lang.UNIXProcess.forkAndExec(Native Method)
at java.lang.UNIXProcess.<init>(UNIXProcess.java:185)
at java.lang.ProcessImpl.start(ProcessImpl.java:134)
at java.lang.ProcessBuilder.start(ProcessBuilder.java:1023)
... 7 more
 
最终发现生成的shell文件没有加上可执行文件的executable属性,以及shell命令文件头:
 
file.executable()
!/bin/bash
 
应用过程中后面还有很多坑,等着我们去填......

Apache common exec包的相应使用总结的更多相关文章

  1. apache common包下的StringUtils的join方法

    apache common包下的StringUtils的join方法: 关键字:java string array join public static String join(Iterator it ...

  2. Apache Common DbUtils

    前段时间使用了Apache Common DbUtils这个工具,在此留个印,以备不时查看.大家都知道现在市面上的数据库访问层的框架很多,当然很多都是包含了OR-Mapping工作步骤的 例如大家常用 ...

  3. apache commons Java包简介

    更多信息,请参考:http://commons.apache.org/ 一.Commons BeanUtils说明:针对Bean的一个工具集.由于Bean往往是有一堆get和set组成,所以BeanU ...

  4. apache commons io包基本功能

    1. http://jackyrong.iteye.com/blog/2153812 2. http://www.javacodegeeks.com/2014/10/apache-commons-io ...

  5. WEB文件上传之apache common upload使用(一)

    文件上传一个经常用到的功能,它有许多中实现的方案. 页面表单 + RFC1897规范 + http协议上传 页面控件(flash/html5/activeX/applet) + RFC1897规范 + ...

  6. Apache源码包安装和子配置文件介绍--update.2014-12-5

    安装apache: 官网:http://httpd.apache.org/download.cgi#apache24 1.wget http://mirror.bit.edu.cn/apache//h ...

  7. golang中os/exec包用法

    exec包执行外部命令,它将os.StartProcess进行包装使得它更容易映射到stdin和stdout,并且利用pipe连接i/o. 1.func LookPath(file string) ( ...

  8. org.apache.common.io-FileUtils详解

    org.apache.common.io---FileUtils详解 getTempDirectoryPath():返回临时目录路径; public static String getTempDire ...

  9. Java网络编程:利用apache的HttpClient包进行http操作

    本文介绍如何利用apache的HttpClient包进行http操作,包括get操作和post操作. 一.下面的代码是对HttpClient包的封装,以便于更好的编写应用代码. import java ...

随机推荐

  1. python中pickle模块与base64模块的使用

    pickle模块的使用 pickle模块是python的标准模块,提供了对于python数据的序列化操作,可以将数据转换为bytes类型,其序列化速度比json模块要高. pickle.dumps() ...

  2. node csrf 防御 待续

    csrf 防御 token 与 ajax 主要是在cookie添加随机数, 因为攻击者 无法访问第三方网站的 cookie,  加上httponly, 即使是xss也无法访问了 也可以在页面上嵌入一个 ...

  3. Snap Impression (by quqi99)

    版权声明:可以任意转载,转载时请务必以超链接形式标明文章原始出处和作者信息及本版权声明 (http://blog.csdn.net/quqi99) Snap一是把应用隔离在沙盒之内,保证不同应用之间或 ...

  4. 【javascript】js处理字符串

    javascript常用方法锦集: 处理字符串 在Javascript除了使用数组和对象 String.replace(regexp | replaceThis,replaceWith |callba ...

  5. 升级到XE10

    下午抽空从XE7升级到XE10,用的是lsuper大侠的Dx10Update1_23.0.21418.4207,用到的控件基本装全乎了. 过程中也碰到点问题,记录下子. 1. cnPack结构匹配线与 ...

  6. hiho1613 墨水滴

    对不起,太弱了.................想了一下午

  7. Eclipse中注释方法操作(两种)

    Eclipse 中的两种注释方法:(1)多行注释 /* */ (2)单行注释 // 多行注释操作方法. 选中注释部分-菜单栏右上角 source: Add block comment.必须选中需要注释 ...

  8. 每天一个linux命令(文件操作):【转载】find命令之xargs

    在使用 find命令的-exec选项处理匹配到的文件时, find命令将所有匹配到的文件一起传递给exec执行.但有些系统对能够传递给exec的命令长度有限制,这样在find命令运行几分钟之后,就会出 ...

  9. POJ3422 Kaka's Matrix Travels 【费用流】*

    POJ3422 Kaka's Matrix Travels Description On an N × N chessboard with a non-negative number in each ...

  10. lua不支持的泛型方法

    1.没有泛型约束 2.缺少带约束的泛型参数 3.泛型约束必须为class /// <summary> /// 不支持生成lua的泛型方法(没有泛型约束) /// </summary& ...