【问题标题】:Apache Commons exec PumpStreamHandler continuous inputApache Commons exec PumpStreamHandler 持续输入
【发布时间】:2018-10-28 02:39:26
【问题描述】:

我正在尝试使用 Apache Commons exec 解决与命令行进程的交互问题。我坚持使用以下代码:

ByteArrayOutputStream out = new ByteArrayOutputStream();
ByteArrayOutputStream ins = new ByteArrayOutputStream();
OutputStreamWriter ow = new OutputStreamWriter(ins);
BufferedWriter writer = new BufferedWriter(ow);
ByteArrayInputStream in = new ByteArrayInputStream(ins.toByteArray());
PumpStreamHandler psh = new PumpStreamHandler(out, null, in);
CommandLine cl = CommandLine.parse(initProcess);
DefaultExecutor exec = new DefaultExecutor();
DefaultExecuteResultHandler resultHandler = new DefaultExecuteResultHandler();
exec.setStreamHandler(psh);
try {
    exec.execute(cl, resultHandler);
    int i = 0;
    while (true) {
        String o = out.toString();
        if (!o.trim().isEmpty()) {
            System.out.println(o);
            out.reset();
        }
        // --- PROBLEM start ---
        if (i == 3) {
            writer.write(internalProcessCommand); 
            // string with or without trailing \n, both tested
            writer.flush();
            writer.close();
            // tested even ins.write(internalProcessCommand.getBytes())
        }
        // --- PROBLEM end ---
        Thread.sleep(3000);
        i++;
    }
} catch (ExecuteException e) {
    System.err.println(e.getMessage());
}

我希望我的代码是清晰的。我不断阅读out 并在清除流时 3 秒后打印。问题是输入到in 传递给PumpStreamHandler。我需要从代码本身连续动态地传递进程命令,就好像我正在通过 CLI 与进程交互一样。当我简单地使用System.in 作为PumpStreamHandler 参数时,我可以很好地从控制台编写进程命令。我怎样才能设法从代码中传递相同的结果?

编辑: 我也尝试连接PipedInputStreamPipedOutputStream接收数据,但似乎只有在关闭PipedOutputStream后才能读取数据,这使得它无法重复使用,因此我无法实现交互。

编辑 2: 自己解决了。下面的答案中的解决方案。豪。 :-)

【问题讨论】:

    标签: java inputstream apache-commons outputstream apache-commons-exec


    【解决方案1】:

    根据@champagniac 给出的答案,我创建了一个简单的修复程序,它仅通过替换PumpStreamHandler 来引入额外的刷新:

    public class PumpStreamHandlerFixed extends PumpStreamHandler
    {
      public PumpStreamHandlerFixed()
      {
        super();
      }
    
      public PumpStreamHandlerFixed(OutputStream out, OutputStream err, InputStream input)
      {
        super(out, err, input);
      }
    
      public PumpStreamHandlerFixed(OutputStream out, OutputStream err)
      {
        super(out, err);
      }
    
      public PumpStreamHandlerFixed(OutputStream outAndErr)
      {
        super(outAndErr);
      }
    
      @Override
      protected Thread createPump(InputStream is, OutputStream os, boolean closeWhenExhausted)
      {
        os = new AutoFlushingOutputStream(os);
    
        final Thread result = new Thread(new StreamPumper(is, os, closeWhenExhausted), "Exec Stream Pumper");
        result.setDaemon(true);
        return result;
      }
    }
    
    class AutoFlushingOutputStream extends OutputStream
    {
      private final OutputStream decorated;
    
      public AutoFlushingOutputStream(OutputStream decorated)
      {
        this.decorated = decorated;
      }
    
      @Override
      public void write(byte[] b, int off, int len) throws IOException
      {
        this.decorated.write(b, off, len);
        this.decorated.flush();
      }
    
      @Override
      public void write(int b) throws IOException
      {
        this.decorated.write(b);
        this.decorated.flush();
      }
    
      @Override
      public void close() throws IOException
      {
        this.decorated.close();
      }
    
      @Override
      public void flush() throws IOException
      {
        this.decorated.flush();
      }
    }
    

    【讨论】:

      【解决方案2】:

      一种解决方案是复制PumpStreamHandlerStreamPump的实现,比如ImmediatePumpStreamHandler和ImmediateStreamPump,并进行以下两个更改:

      • 在 ImmediateStreamPump 的第 108 行的 os.write(buf, 0, length); 之后直接添加 os.flush();
      • 在 ImmediatePumpStreamHandler 的第 269 行将 new StreamPumper(...) 更改为 new ImmediateStreamPump(...)

      【讨论】:

        【解决方案3】:

        好的,我使用内置工具而不是外部库解决了这个问题。多亏了 Process' InputStream 的独立线程,我能够实现我的目标:

        ProcessBuilder builder = new ProcessBuilder(command);
        Process process = builder.start();
        
        BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(process.getOutputStream()));
        StreamReader outputReader = new StreamReader(process.getInputStream(), System.out);
        outputReader.start();
        StreamReader err = new StreamReader(process.getErrorStream(), System.err);
        err.start();
        
        for (int i = 0; i < 3; i++) {
            Thread.sleep(5000);
            writer.write(internalProcessCommand + "\n");
            writer.flush();
        }
        writer.write("exit\n");
        writer.flush();
        
        while (process.isAlive()) {
            System.out.println("alive?");
            Thread.sleep(100);
        }
        System.out.println("dead");
        outputReader.shutdown();
        err.shutdown();
        

        流读取器:

        class StreamReader extends Thread {
        
            private AtomicBoolean running = new AtomicBoolean(false);
            private InputStream in;
            private OutputStream out;
        
            public StreamReader(InputStream in, OutputStream out) {
                this.in = in;
                this.out = out;
                running.set(true);
            }
        
            @Override
            public void run() {
                Scanner scanner = new Scanner(in);
                PrintWriter writer = new PrintWriter(out, true);
                while (running.get()) {
                    if (scanner.hasNextLine()) {
                        writer.println(scanner.nextLine());
                    }
                }
                scanner.close();
            }
        
            public void shutdown() {
                running.set(false);
            }
        
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2011-11-12
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-11-07
          相关资源
          最近更新 更多