我在使用 Java 1.7.0_21 和 _25 在 Mac OS X 10.8.4 上运行的 Java Web Start 应用程序中尝试使用 ProcessBuilder 执行特定命令时遇到了问题。
我使用 ProcessBuilder 执行“/usr/sbin/system_profiler”命令,以获取有关客户端机器的一些信息(例如,cpu 名称、# 处理器等)。
通过以下方式启动 Web Start 应用程序时,该命令会正确执行:
- 仅带有 URL 参数的命令行 javaws
- 命令行 javaws 与 Web Start 创建的应用程序包具有相同的参数
- Java Web Start 系统首选项缓存应用程序对话框
通过以下方式启动该命令时未正确执行:
- 首次启动时由 Web Start 创建的 Mac 应用程序包的快捷方式
在使用 Web Start 快捷方式启动的情况下,“system_profiler”进程会创建一个子“system_profiler”,它会创建它自己的子进程,依此类推,直到最终我收到以下错误(可能是进程可用性耗尽时):
Failed to gather report from '/usr/sbin/system_profiler -xml SPHardwareDataType -detailLevel full', exception: couldn't fork: errno 35
注意:即使“/usr/sbin/system_profiler”仅使用“SPHardwareDataType”参数启动,也会发生上述错误。
该问题似乎特定于“system_profiler”命令,因为它不会发生在其他命令的(公认很小)随机抽样中。
我还尝试了几种不同的方法来执行命令:
/usr/sbin/system_profiler
/usr/sbin/system_profiler SPHardwareDataType
/bin/sh -c /usr/sbin/system_profiler
/bin/sh -c /usr/sbin/system_profiler SPHardwareDataType
/bin/tcsh -c /usr/sbin/system_profiler
所有组合都给出相同的行为。
应用程序包 Info.plist 在“WebStartArgs”键下包含以下键值对:
- Item 0: -localfile
- Item 1: -online
- Item 2: -J-Djnlp.application.href=http://localhost:8080/test/test.jnlp
- Item 3: /Users/blah/Library/Application Support/Oracle/Java/Deployment/cache/6.0/40/744be8-7ce41b1a
如上所述,我尝试使用相同的参数通过命令行启动,但没有出现问题。
这是一个展示该问题的精简测试应用程序:
import java.io.*;
import java.util.Arrays;
public class TestProcessBuilder {
public static void main(String[] commands) throws IOException, InterruptedException {
System.out.println("Args: " + Arrays.toString(commands));
final ProcessBuilder pb = new ProcessBuilder(commands).redirectErrorStream(true);
System.out.println("Environment: " + pb.environment());
final Process p = pb.start();
final StringBuilder buffer = new StringBuilder();
Thread t = new Thread() {
@Override
public void run() {
InputStream stdoutStream = null;
try {
stdoutStream = new BufferedInputStream(p.getInputStream());
for(;;) {
int c = stdoutStream.read();
if(c == -1)
break;
buffer.append((char) c);
}
System.out.println("stdout buffer: " + buffer);
} catch(IOException e) {
throw new RuntimeException(e);
} finally {
close(stdoutStream);
}
}
};
t.start();
int result = p.waitFor();
t.join();
if(result != 0) {
throw new RuntimeException("Error result: " + result + " - " + buffer);
}
}
private static void close(Closeable closeable) {
try {
if(closeable != null)
closeable.close();
} catch(IOException e) {
e.printStackTrace();
}
}
}
作为说明,我还尝试不使用单独的线程来处理进程输出
这是简单测试应用程序的 JNLP 文件:
<jnlp spec="1.0+" codebase="http://localhost:8080/test" href="test.jnlp">
<information>
<title>Test Process Builder</title>
<vendor>Test Vendor</vendor>
<homepage href="http://www.test.com" />
<description>Launches Test Process Builder</description>
<description kind="short">Test Process Builder</description>
<shortcut online="true">
<desktop />
</shortcut>
</information>
<security>
<all-permissions/>
</security>
<update check="always" policy="always"/>
<resources>
<java href="http://java.sun.com/products/autodl/j2se" version="1.7.0_21+"/>
<property name="jnlp.versionEnabled" value="false"/>
<jar href="lib/test.jar" download="eager" main="true"/>
</resources>
<application-desc main-class="netmedical.util.TestProcessBuilder">
<argument>/bin/sh</argument>
<argument>-c</argument>
<argument>/usr/sbin/system_profiler SPHardwareDataType</argument>
</application-desc>
</jnlp>
我还尝试将 JNLP 转换为引用本地文件系统:
codebase="file://localhost/Library/Tomcat/webapps/test"
并发生相同的行为。
启动 Mac 应用程序包时,唯一的区别必须与运行时环境有关,但我不知道这种区别可能是什么。
有什么问题吗:
- 从 Java 应用程序启动进程的方式是什么?
- 处理输出的方式?
- Web Start 部署配置?
- 其他?