我想看到一个由Java调用的外部进程创建的窗口

时间:2015-09-22 02:08:02

标签: java multithreading

我不认为我想要使用的程序在与地震学无关的人中很有名。但我想要使用的是地震图的实用软件。 无论如何,它是一个基本上我们在shell中使用的交互式软件。

(SAC表示地震分析代码)

  

SAC>读"文件名"

     

SAC>积

然后它将创建一个显示波形的窗口。

我想通过ProcessBuilder和Process在Java中执行此操作。 我实际上并不关心我使用什么类,只是我知道他们只做这样的事情。

我创建了一个为SAC提供进程的类。 标准输出和错误分别由另一个线程读取, 当我用Google搜索它时,它告诉我们处理问题的一个可能原因是 与那些有关。 (即使SAC没有如此多地返回输出错误......)

在我启动那些threads.start()之后,我写了#34;读取文件名"和"情节" 标准输入,但没有任何反应。某个程序退出时会出现一个窗口。

这是我的代码。

public static void main(String[] args){
 try(Sac sac = Sac.createProcess()){ 
   // Class Sac governs a Process for SAC
    sac.inputCMD("r abc.R"); 
   // inputCMD is writing in standard input and flush immediately
    sac.inputCMD("plot");

      for (;;) // this is just for time to see what is going on
        ;
     }
}

我会再说一遍 当我杀死Java运行时,窗口有时会弹出并很快消失。

我认为这与线程问题有关。 因为当我从另一个线程调用SAC#inputCMD(" plot")时,每次都会立即弹出一个窗口。

我在main中创建SAC是一个问题吗?

----添加---- 我提到了其他线程的顺序。但它通常不起作用......

这是Class Sac和ExternalProcess

public class Sac extends ExternalProcess implements Closeable {

 Sac(Process process) {
     super(process);
     standardInput = new PrintWriter(super.standardInput);
 }

  public static Sac createProcess() throws IOException {
      ProcessBuilder builder = null;
      if (checkEnvironment())
          builder = new ProcessBuilder("sac");
      else
          throw new RuntimeException("No sac in PATH or No SACAUX is set.");

      return new Sac(builder.start());
  }

/**
 * Make an order to Sac
 * 
 * @param line
 *            command line for SAC
 */
  public void inputCMD(String line) {
    standardInput.println(line);
    standardInput.flush();
}

  @Override
  public void close() {
    try {
        standardInput.println("q");
        standardInput.flush();
        standardInput.close();
        process.waitFor();
        standardOutput.join();
        standardError.join();
    } catch (Exception e) {
        e.printStackTrace();
    }

  }

  private static boolean checkEnvironment() {
    // String sachome = System.getenv("SACHOME");
    boolean sacExistence = System.getenv("PATH").contains("/sac/bin");
    String sacaux = System.getenv("SACAUX");
    return sacExistence && sacaux != null;
  }

/**
 * Input for Sac
 */
  private PrintWriter standardInput;
}

这是ExternalProcess。

public class ExternalProcess {
 /**
  * {@link Stream} for standard output
  */
  protected InputStreamThread standardOutput;

 /**
  * {@link Process} for Sac
  */
  protected Process process;

 /**
  * {@link Stream} for standard error
  */
  protected InputStreamThread standardError;

 /**
  * connected to standard input
  */
  protected OutputStream standardInput;

  ExternalProcess(Process process) {
      this.process = process;
      standardOutput = new InputStreamThread(process.getInputStream());
      standardError = new InputStreamThread(process.getErrorStream());
      standardError.start();
      standardOutput.start();
      standardInput = process.getOutputStream();

  }

  public static ExternalProcess launch(String... command) throws IOException {
     ProcessBuilder builder = new ProcessBuilder(command);
     return new ExternalProcess(builder.start());

 }

 /**
  * @return {@link OutputStream} connected to a standard input to the
  *         process
  */
 public OutputStream getStandardInput() {
    return standardInput;
 }

 /**
  * @return {@link InputStreamThread} connected to a standard output to the
  *         process
  */
  public InputStreamThread getStandardOutput() {
     return standardOutput;
  }

 /**
  * @return {@link InputStreamThread} connected to a standard error to the
  *         process
  */
  public InputStreamThread getStandardError() {
    return standardError;
  }

public static ExternalProcess launch(List<String> command) throws IOException {
    ProcessBuilder builder = new ProcessBuilder(command);
    return new ExternalProcess(builder.start());

}

  public int waitFor() {
     try {
         int process = this.process.waitFor();
         standardError.join();
         standardOutput.join();
         return process;
     } catch (Exception e) {
         throw new RuntimeException();
     }

  }

}

最后是InputStreamThread

public class InputStreamThread extends Thread {

/**
 * if the stream is closed
 */
  private boolean closed;

 /**
  * Wait until the inputstream is closed and return String[]
  * @return {@link String}[] from input stream
  */
  public String[] waitAndGetString(){
    try {
        while(!closed)
            Thread.sleep(100);
    } catch (Exception e) {
        e.printStackTrace();
    }
    return list.toArray(new String[list.size()]);
 }

 @Override
 public void run() {
    try(BufferedReader br = new BufferedReader(inputStreamReader)) {
        for (;;) {
            String line = br.readLine();
            if (line == null)
                break;
            list.add(line);
            //System.out.println(line+" "+list.size());
        }

    } catch (IOException e) {
        throw new RuntimeException(e);
    } finally{
        closed = true;
    }
 }



 /**
 * List of String from the stream 
 * 文字列の取得
 * @return {@link List} of {@link String} from the {@link InputStream}
  */
 public List<String> getStringList() {
    return list;
 }

 private List<String> list = new ArrayList<>();



 private InputStreamReader inputStreamReader;


 public InputStreamThread(InputStream is) {
    inputStreamReader = new InputStreamReader(is);
 }


}

1 个答案:

答案 0 :(得分:0)

所以,一个问题和一个答案。问题:你关心标准输出/标准错误的内容吗?如果你不这样做,我建议你开始使用管道,这引导我回答:

你的ExternalProcess类在那时变得更简单了(不需要乱用输入读取回显线程等)。

此外,您谈论的其他事情似乎表明多个线程可能有权访问ExternalProcess的实例 - 这意味着您应该标记您的&#34;输入&#34;方法同步。

我最终得到了这个:

package so;

import java.io.IOException;
import java.io.PrintWriter;

public class ExternalProcess {

    private final Process process;
    private final PrintWriter commandWriter;

    public ExternalProcess(String... command) throws IOException {

        process = new ProcessBuilder(command)
                .redirectError(ProcessBuilder.Redirect.INHERIT)
                .redirectOutput(ProcessBuilder.Redirect.INHERIT)
                .redirectInput(ProcessBuilder.Redirect.PIPE)
                .start();

        commandWriter = new PrintWriter(process.getOutputStream());

    }

    public synchronized ExternalProcess inputCMD(char... chars) {

        if (chars != null && chars.length > 0) {
            commandWriter.write(chars);
        }
        return this;
    }

    public synchronized ExternalProcess inputCMD(String line) {

        commandWriter.println(line);
        commandWriter.flush();
        return this;
    }

    public void waitFor() throws InterruptedException {

        process.waitFor();
    }

    public void stop() {

        process.destroy();
    }

}

然后我写了一个小测试来推送我的常规shell,包括用VI做一些无意义的编辑。

package so;

import java.io.IOException;

public class TestMain {

    public static void main(String... args) throws IOException, InterruptedException {

        ExternalProcess externalProcess = new ExternalProcess("/bin/bash");
        externalProcess.inputCMD("ls -lah");
        externalProcess.inputCMD("rm /tmp/foo");
        externalProcess.inputCMD("vi /tmp/foo");
        externalProcess.inputCMD("i hello world.");
        externalProcess.inputCMD((char) 0x1B);
        externalProcess.inputCMD(":wq!");

    }

}

似乎工作正常。

如果您遇到问题&#34; sac&#34;作出回应 - 它可能与某些奇怪的方式中的期待事物/ dev / tty有关。

请注意,其中一些不可避免的特定于平台 - 如果你的能力不同而不是在一个不合适的世界中,我会把这个转化为你的宇宙,为读者做一个练习。