Apache Commons Exec主要用于执行外部进程的命令。Exec目前最新版本是1.3,最低要求Java5以上。
用Java执行外部进程命令也是比较常见的一种需求,这种操作依赖特定操作系统,需要我们了解特定系统的行为,例如在Windows上使用cmd.exe。想要可靠地执行外部进程还需要在执行命令之前或之后处理环境变量。
Apache Commons Exec就是为了处理上面概述的各种问题。而且代码实现起来也比较简单。
maven坐标
1 |
|
下面简单介绍一下用法。
同步调用
同步调用系统命令后会阻塞当前线程,直到获取到结果。
使用JDK写法
1 |
|
等等,这么写其实有坑。如果执行一个安装脚本会在控制台输出大量内容,这时可能会导致进程卡死(其实是一直阻塞状态)。
这是由于缓冲区满了,无法写入数据,导致线程阻塞,对外现象就是进程无法停止,也不占资源,什么反应也没有。
这种情况可以单独启动一个线程去读取输入流的内容,避免缓冲区占满,示例如下:
1 |
|
如果是异常信息打印过多则处理process.getErrorStream()。
使用commons写法
commons-exec的command不需要考虑执行环境了,比如windows下不需要添加”cmd /c “的前缀。可以使用自定义的流来接受结果,比如使用文件流将结果保存到文件,使用网络流保存到远程服务器上等。下面的例子为了简单直接使用字节流去接收(如果结果非常大就不要用字节流了,容易内容溢出)。
1 |
|
异步调用
使用JDK写法
JDK自带的Runtime的API不支持异步执行,如果要异步拿到执行结果需要自己单独创建线程不断轮询进程状态然后通知主线程,下面看一个例子。例子力求简单,所以很多细节不是很严谨,只看大体思路即可(如果要实现exec方便的API需要更多的代码来实现)。
1 |
|
使用commons写法
commons-exec原生支持异步调用,下面直接看例子。
1 |
|
监控
commons-exec支持监控外部进程的执行状态并做一些操作,如超时,停止等。
在使用Runtime.getRuntime().exec(cmd)执行某些系统命令,如nfs共享的mount时,会由于nfs服务异常等原因导致进程阻塞,使程序没法往下执行,而且也无法捕获到异常,相当于卡死在了。这时如果有超时放弃的功能就好了,当然超时功能可以自己轮询process.exitValue()去实现,稍微麻烦一些,这里就不做示例了。
commons-exec主要通过ExecuteWatchdog类来处理超时,下面看例子
1 |
|
ExecuteWatchdog还支持销毁进程,只需调用destroyProcess(),由于ExecuteWatchdog是异步执行的,所以调用后不会马上停止。使用起来也比较简单就不做说明了。
总结
commons-exec屏蔽了不同操作系统的命令差异,解决了Runtime缓冲区问题导致的线程卡死,同时支持超时和等,用来代替JDK的Runtime API是非常不错的选择。