</p>

2007.01.06 旧文整理 作者: BeanSoft

这个东西是我当时写了用来调试偶们的短信平台的, 因为当时还不知道 Eclipse, 所以为了反复测试短信应用, 不得不同时启动四个Java进程才能工作: app.bat – 应用服务器, sim.bat – 手机模拟器,sonsole.bat – 控制台,um.bat – 用户管理器. 原来的时候呢, 要反复切换四个 DOS 窗口来查看开发的应用产生的日志, 而且错误信息也不方便浏览. 后来通过使用 Java 中的进程输出流成功的解决了这个疑难问题, 并制作了一个 Java 进程启动控制中心, 能够运行, 终止, 启动这些进程, 对进程的输出进行加颜色区别显示, 使用配置文件来方便的修改/添加新的进程启动参数, 但是没有实现拦截输入流的功能. 如下图所示:  </p>

image  

下载(含源码): </span>

ProcessViewer.zip 204KB

讲解: 首先关于拦截流的基础知识可以参考 BlogJava 的两篇文章: Java调用外部进程并拦截输入输出流--Java IDE Console解密(上篇) Java调用外部进程并拦截输入输出流--Java IDE Console解密(下篇), 倒省了我费口舌来说那么多了, 呵呵.

首先上例中的进程代码为: java -cp ./classes TestWithOutput

再看TestWithOutput.java:

</p>

</span>/**
*

Title: TestWithOutput


*

Description: Java 进程查看器辅助工具, 命令行输出内容进行测试


*

Copyright: Copyright (c) 2003


*

Company: BeanSoft Studio


*
@author 刘长炯
*
@version 1.0
 
*/ </p>

</span>public class TestWithOutput { </p>

  </span>public TestWithOutput() throws Exception {
    // Dead lock, for test purpose only
    for(int i = ;; i++) {
      Thread.currentThread().sleep(
500L);
      System.out.println(
"输出:" + i);
      System.err.println(
"错误:" + i);
    }
  } </p>

  </span>public static void main(String[] args) throws Exception {
    TestWithOutput testWithOutput1
= new TestWithOutput();
  } </p>

}</span></div>

可以看到我们成功的拦截了输出和错误流的内容并用不同的颜色加以区分.

我们的内核我依稀记得是搬移自 BeanShell 的 JConsole(很老的版本的了), 它的类名叫 JConsole, 实现了输出到彩色字符串的功能. 接下来的核心就是对 Process 的处理了:

package studio.beansoft.system; </p>

</span>import java.io.*; </p>

</span>/** 

Title: Process Viewer – ProcessHandler


Description: Java 进程查看器 – 进程处理器, 封装了对进程的处理工作


Copyright: Copyright (c) 2003


Company: BeanSoft Studio


 
*/
public class ProcessHandler{
 
/** 命令字符串 */
 
private String commandString = </s>"";
 
/** 进程标题 */
 
private String processTitle = "";
 
/** 当前引用的进程 */
 
private Process process;
 
/** 输出缓冲区 */
 
/** 用来显示输出的 javax.swing.JTextArea */
 
/** Show whether the process still active */
 
boolean isInProcess = false;
 
/** 图形化的输出控制台 */
 
private JConsole jConsole; </p>

  </span>public ProcessHandler() {
  } </p>

  </span>/* For test purpose only. */
 
public static void main(String[] args) {
    ProcessHandler processHandler1
= new ProcessHandler();
    processHandler1.setCommandString(
"java TestWithOutput");
   
//processHandler1.setBufferSize(100);
    javax.swing.JFrame frame = new javax.swing.JFrame();
    studio.beansoft.system.JConsole console
= new studio.beansoft.system.JConsole();
    frame.getContentPane().add(console);
    frame.setSize(
600, 400);
    frame.show();
    processHandler1.setJConsole(console);
    processHandler1.startProcess();
  } </p>

  </span>/**
   * 根据给定命令启动进程并创建读取参数的线程.
  
*/
 
public void startProcess() {
     
// 该进程已经运行了, 不能重复运行.
      if (process != null) return; </p>

    isInProcess </span>= true; // Set the process active flag be valid
    Thread processThread = new Thread() {
     
public void run() {
       
try {
          printNote(
"正在启动进程, 命令:"" + getCommandString() + "", 名称:" + getProcessTitle() + "n"); </p>

          </span>//final ByteArrayOutputStream out = new ByteArrayOutputStream();
          process = Runtime.getRuntime().exec(getCommandString());
         
final BufferedReader pin = new BufferedReader(
             
new InputStreamReader(process.getInputStream()));
          OutputStream processOut
= process.getOutputStream();
         
final BufferedReader perr = new BufferedReader(
             
new InputStreamReader(process.getErrorStream()));
         
int data; </p>

          </span>// Because pin and perr are streams connect to the same process,
         
// so when you read out one staream, the another stream is also closed,
         
// we only can start both reading in a thread
          Thread errReadThread = new Thread() {
           
public void run() {
             
try {
                String line;
               
while ( (line = perr.readLine()) != null) {
                  printError(line
+ "n");
                }
              }
             
catch (Exception ex) {
                ex.printStackTrace();
              }
            }
          }; </p>

          errReadThread.start();

          </span>// 进程输出流的读取
          String line = null;
         
while ( (line = pin.readLine()) != null) {
              printOutput(line
+ "n");
          } </p>

          isInProcess </span>= false; // End the display thread

         
int exitValue = process.exitValue();
         
if (exitValue != ) {
           
while ( (line = perr.readLine()) != null) {
              printError(line
+ "n");
            }
     &
#160;      printNote(
"n进程退出时发生了错误, 错误码为:" + exitValue + "n");
          }
         
else {
            printNote(
"进程已经正常退出.n");
          } </p>

          process.waitFor();
          process.destroy();
          process </span>= null;
         
//System.out.println(process);
        } catch (Exception ex) {
          printNote(
"运行命令时发生错误:" + ex + "n");
          stopProcess();
        }
      }
    };
    processThread.start();
  } </p>

  </span>/**
   * 结束当前正在执行的进程.
  
*/
 
public void stopProcess() {
   
if (process != null) {
     
try {
        printNote(
"正在尝试终止进程 " + getProcessTitle() + "n");
        process.destroy();
        printNote(
"进程已经被终止了.n");
        process
= null;
      }
     
catch (Exception e) {
        e.printStackTrace();
      }
    }
  } </p>

  </span>/** 以红色输出错误信息 */
 
private void printError(String message) {
    getJConsole().print(message, java.awt.Color.red);
  } </p>

  </span>/** 以橙色输出注释信息 */
 
private void printNote(String note) {
    getJConsole().print(note, java.awt.Color.orange);
  } </p>

  </span>/** 向控制台转发输出 */
 
private void printOutput(String output) {
    getJConsole().print(output, java.awt.Color.blue);
  } </p>

  </span>/**
   * 重新开始进程(先尝试停止, 然后再开始)
  
*/
 
public void restartProcess() {
    stopProcess();
    startProcess();
  } </p>

  </span>/**
   * 返回进程要执行的命令的字符串.
   *
@return commandString 属性
  
*/

 
public String getCommandString() {
   
return commandString;
  } </p>

  </span>/**
   * 设置 commandString 属性.
   *
@param commandString 要执行的命令的字符串
  
*/
 
public void setCommandString(String commandString) {
   
this.commandString = commandString;
  } </p>

  </span>/**
   * 返回进程的标题.
   *
@return processTitle 属性
  
*/
 
public String getProcessTitle() {
   
return processTitle;
  } </p>

  </span>/**
   * 设置进程的标题.
   *
@param processTitle 进程标题字符串
  
*/
 
public void setProcessTitle(String processTitle) {
   
this.processTitle = processTitle;
  } </p>

  </span>/**
   * 返回当前输出显示的文本区的实例.
   *
@return 当前输出显示的文本区对象.
  
*/
 
public JConsole getJConsole() {
   
return jConsole;
  } </p>

  </span>/**
   * 设置输出显示的 JTextArea.
   *
@param jConsole 指定的 JTextArea
  
*/
 
public void setJConsole(JConsole jConsole) {
   
this.jConsole = jConsole;
   
if(jConsole != null) {
     
//System.setErr(getJConsole().getErr());
     
//System.setOut(getJConsole().getOut());
    }
  } </p>

  </span>/**
   * 返回管理的进程的实例.
   *
@return 当前进程的实例.
  
*/
 
public Process getProcess() {
   
return process;
  } </p>

  </span>/**
   * 设置当前管理的进程示例.
   *
@param process 被管理的进程示例.
  
*/
 
public void setProcess(Process process) {
   
this.process = process;
  } </p>

}</span></div>

在 startProcess() 的时候对输出进行拦截, 并根据信息的类型输出消息. 进程结束的时候就输出进程已经完成的信息.

OK, enjoy it!

转载请注明:WebLogic Android 博客 » Java调用外部进程并拦截输出流的实例: 进程管理器1.0版(原创)