2

我在使用 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 部署配置?
  • 其他?
4

0 回答 0