最近在写一个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 Commons Exec 是一个 Java 库,它为在 Java 应用程序中执行外部命令提供了强大的支持。这个库弥补了 Java 标准库 `Runtime.exec()` 方法的一些不足,尤其是在处理命令输出、错误处理和进程管理方面。`cmdexec...
hive 开发UDF 使用maven工程 引发jar包缺失 hive 开发UDF 使用maven工程 引发jar包缺失
hive java开发驱动包列表hive-common-2.3.4.jarhive-exec-2.3.4.jarhive-jdbc-2.3.4.jarhive-llap-client-2.3.4.jarhive-llap-common-2.3.4.jarhive-llap-server-2.3.4.jarhive-llap-tez-2.3.4.jarhive-metastore-...
Java common相关开发包是Java开发中的重要组成部分,它们提供了丰富的工具类和实用功能,极大地提升了开发效率和代码质量。在本篇文章中,我们将深入探讨这些常见的开发包,以及它们在实际开发中的应用。 首先,...
Hive是Apache Hadoop生态系统中的一个数据仓库工具,它允许用户使用SQL(称为HQL,Hive Query Language)对存储在HDFS上的大型数据集进行分析。Hive 2.1.1是Hive的一个重要版本,而CDH6.3.2是Cloudera发行的商业版...
包括以下jar包: Attributes Beanutils Betwixt Chain CLI Codec Collections Configuration Daemon DBCP DbUtils Digester Discovery EL Email Exec FileUpload IO JCI Jelly JEXL JXPath Lang Launcher Logging ...
由于在你的场景中不使用Maven这样的项目管理工具,你需要手动收集和管理这些jar包。以下是对标题和描述中涉及的知识点的详细解释: 1. **Hive简介**:Hadoop Hive是一个基于Hadoop的数据仓库工具,可以将结构化的...
Common-Pool2.jar是Apache Commons Pool库的第二个主要版本,这是一个对象池设计模式的实现,用于提高对象的重用性,减少创建和销毁对象的开销。在Jedis中,它被用于管理Redis连接,通过复用已建立的连接,避免频繁...
总结来说,为了在IntelliJ IDEA中集成Apache Hive,我们需要准备Hive的JDBC驱动和其他相关依赖库,将它们添加到项目中,然后编写Java代码来连接Hive服务器并执行查询。通过这种方式,开发者可以在熟悉的IDE环境中...
11. **jdbc-driver的其他依赖**: 取决于你所使用的JDBC驱动,例如MySQL、Oracle等,如果Hive数据库是建立在这些关系型数据库之上,那么相应的JDBC驱动也是必要的。 在实际开发中,你需要将这些jar包添加到项目的类...
在实际使用时,确保JAR包版本与你的Hive和Hadoop版本兼容至关重要,因为不匹配的版本可能导致运行时错误或功能异常。 总的来说,"hive连接所需jar包"是一个包含了连接到Hive服务器所必需的库的集合,这对于开发人员...
在大数据处理领域,Hive是一个基于Hadoop的数据仓库工具,它允许用户使用SQL(HQL,Hive Query Language)查询和管理存储在Hadoop分布式文件系统(HDFS)中的大量结构化数据。Hive 1.1.0是Hive的一个版本,提供了...
Apache作为全球使用最为广泛的Web服务器之一,在提供静态网页服务的同时,也支持动态网页处理功能,如通过CGI(Common Gateway Interface)、SSI(Server Side Includes)等方式。本文将详细介绍如何配置Apache使其支持...
总结来说,"redis连接池jar jedis+common"是指在Java项目中,利用Jedis客户端和Apache Commons Pool2库实现Redis连接池,以高效地管理和复用数据库连接。通过合理配置和使用,可以显著提升应用的性能和响应速度。
Hive是Apache软件基金会开发的一个数据仓库工具,它允许用户使用SQL查询Hadoop上的大数据集。Hadoop则是分布式存储和计算框架,为大数据处理提供基础架构。这个压缩包包含了Hive 2.1.1和Hadoop 2.6.0的关键组件,但...
在大数据处理领域,Apache Hive是一个基于Hadoop的数据仓库工具,它允许用户使用SQL类查询语言(HQL)来处理存储在Hadoop分布式文件系统(HDFS)中的大规模数据集。本压缩包“hive远程连接工具和jar.zip”包含了用于...
- `curator-framework-2.6.0.jar`:Apache Curator,一个简化ZooKeeper使用的客户端库。 3. **连接步骤**: - 将压缩包中的所有JAR文件添加到DataGrid的类路径中,确保DataGrid可以找到这些驱动。 - 在DataGrid...
在上面的 POM 文件中,我们添加了 Hadoop 和 Hive 的依赖项,包括 Hadoop 的 common 模块和 Hive 的 exec 模块。同时,我们还添加了 Maven 的编译插件和打包插件,以便生成可执行的 JAR 包。 在实现 UDF 函数时,...
Flume 是一个分布式、可靠且可用于有效收集、聚合和移动大量日志数据的系统,而 HDFS(Hadoop Distributed File System)是 Apache Hadoop 项目的核心部分,提供了一个高容错性的分布式文件系统,能够处理PB级别的...
4. 编译完成后,可以在`target`目录下找到编译结果,可以使用`mvn exec:java`或在你的项目中引入编译好的Mahout库,运行示例代码或自定义算法。 四、应用场景与扩展 Mahout广泛应用于数据分析、广告推荐、新闻个性...