Write a console plug-in for eclipse3.5

我第一个JAVA程序终于完成了,是一个eclipse的控制台插件。本插件的目的是,实现一个通用的控制台,当启动一个进程时,将进程的输入输出显示在Console View中。

Console的使用,通过接口来完成。主要的方法是open和close。当然可以继续增加其它一些方法,如改变尺寸等

public interface IMyConsole {
    void open();
    void close();
    ...
}

希望使用时能够尽量简便,例如希望在eclipse中开启一个cmd控制台:

    IMyConsole console;
    ...
    ProcessBuilder builder = new ProcessBuilder("cmd.exe");
    try {
        Process proc = builder.start();
        console = new ConsoleFactory("Windows cmd", null,
            proc.getOutputStream(),
            proc.getInputStream(),
            proc.getErrorStream());
        console.open();
    } catch (IOException e) {
        e.printStackTrace();
    }
    ...

实例化console时候,需要指明控制台的stdin,stdout和stderr。这样就可以灵活用于各种情况,如执行一个进程、socket流、文件流等。同时,也可以指定只使用stdout和stderr,作为纯粹的输出控制台。当然,如果将这些用法再封装一层就好了。实例创建出来后,调用open()方法即可打开控制台。
类的框架如下:

ConsolePlugin

(研究了几个小时UML,结果发现这东西不是一会就能玩明白的,干脆用土办法画图了)

Console插件说白了就是五个数据流:键盘输入,屏幕输出,目标的stdin/stdout/stderr。这几个接起来就搞定了。数据流中间要分别建立线程,用BufferedStream来读写。分别建立线程是防止阻塞,而用Buffer是提高效率。这些东西要是都用C来写,那可就麻烦了。JAVA里面全都帮我搞定了,很舒服。

下面具体实现:

1)新建一个Plug-in Project,选择”Hello, World”模板。这个模板创建一个工具栏上的按钮,点击此按钮后出现一个显示”Hello, World”的Messagebox。以此为基础进行修改。

2)向导中点击Finish后,默认打开的是menifest.mf。点击View下方的Dependences标签,在左侧的Required Plug-ins栏选”Add…”,输入org.eclipse.ui.console,选择”OK”。这是基本的控制台插件。按”ctrl-s”保存。

3)在下方Extensions标签栏中,点击”add…”,找到org.eclipse.ui.consoleFactories,选中后,点击”Finish”。这就增加了一个Console View,用于控制台的操作和显示。按”ctrl-s”保存。

4)点击右侧”class*:”,创建一个新class。使用默认设置即可。

5)然后写代码:

Action.java:

package com.study.terminal.actions;

public class SampleAction implements IWorkbenchWindowActionDelegate {
...
  private IMyConsole console;//加上这个
...
  //修改这个
  public void run(IAction action) {
    ProcessBuilder builder = new ProcessBuilder("cmd.exe");
    try {
      Process proc = builder.start();
      console = new ConsoleFactory("MyConsole", null, proc
        .getOutputStream(), proc.getInputStream(), proc
        .getErrorStream());
      console.open();
    } catch (IOException e) {}
...
  }
}

IMyConsole.java:

package com.study.terminal;

public interface IMyConsole {
    void open();
    void close();
}

ConsoleFactory.java:

package com.study.terminal;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.ui.console.ConsolePlugin;
import org.eclipse.ui.console.IConsole;
import org.eclipse.ui.console.IConsoleListener;
import org.eclipse.ui.console.IConsoleManager;
import org.eclipse.ui.console.IOConsole;
import org.eclipse.ui.console.IOConsoleInputStream;
import org.eclipse.ui.console.IOConsoleOutputStream;

public class ConsoleFactory extends IOConsole implements IMyConsole {
    private IOConsoleInputStream bisInInner;// keyboard input stream
    private BufferedOutputStream bosOutOuter;// stream output to app

    private IOConsoleOutputStream bosOutInner;// stream output to console view
    private BufferedInputStream bisInOuter;// stream input from app's stdout

    private IOConsoleOutputStream bosErrInner;// stream output to console view
    private BufferedInputStream bisErrOuter;// stream input from app's stderr

    private class streamThread implements Runnable {
        private InputStream is;
        private OutputStream os;

        public streamThread(InputStream is, OutputStream os) {
            this.is = is;
            this.os = os;
        }

        public void run() {
            if (is == null || os == null) {
                return;
            }

            byte[] data = new byte[1];
            try {
                while (is.read(data) != -1) {
                    switch (data[0]) {
                    case '\n':
                        os.write(data);
                        os.flush();
                        break;
                    case '\r':
                        // ignore it
                        break;
                    default:
                        os.write(data);
                        break;
                    }
                }
            } catch (IOException e) {
                close();
            }
        }
    }

    private class Listener implements IConsoleListener {
        @Override
        public void consolesAdded(IConsole[] consoles) {
        }

        @Override
        public void consolesRemoved(IConsole[] consoles) {
            for (int i = 0; i < consoles.length; i++) {
                if (ConsoleFactory.this == consoles[i]) {
                    ConsolePlugin.getDefault().getConsoleManager()
                            .removeConsoleListener(this);
                    close();
                }
            }
        }
    }

    public ConsoleFactory(String name, ImageDescriptor imageDescriptor,
            OutputStream stdinOuter, InputStream stdoutOuter,
            InputStream stderrOuter) {
        super(name, imageDescriptor);

        IConsoleManager manager = ConsolePlugin.getDefault()
                .getConsoleManager();
        manager.addConsoleListener(new Listener());
        IConsole[] existing = manager.getConsoles();
        boolean exists = false;
        for (int i = 0; i < existing.length; i++) {
            if (this == existing[i])
                exists = true;
        }

        if (!exists) {
            if (stdinOuter != null) {
                bisInInner = getInputStream();
                bosOutOuter = new BufferedOutputStream(stdinOuter);
            }

            if (stdoutOuter != null) {
                bosOutInner = newOutputStream();
                bisInOuter = new BufferedInputStream(stdoutOuter);
            }

            if (stderrOuter != null) {
                bosErrInner = newOutputStream();
                bisErrOuter = new BufferedInputStream(stderrOuter);
            }

            manager.addConsoles(new IConsole[] { this });
        }
    }

    public void open() {
        IConsoleManager manager = ConsolePlugin.getDefault()
                .getConsoleManager();
        manager.showConsoleView(this);

        // keyboard input thread
        if (bisInInner != null && bosOutOuter != null) {
            streamThread stdinThread = new streamThread(bisInInner, bosOutOuter);
            new Thread(stdinThread).start();
        }

        // stdout of app
        if (bisInOuter != null && bosOutInner != null) {
            streamThread stdoutThread = new streamThread(bisInOuter,
                    bosOutInner);
            new Thread(stdoutThread).start();
        }

        // stderr of app
        if (bisErrOuter != null && bosErrInner != null) {
            streamThread stderrThread = new streamThread(bisErrOuter,
                    bosErrInner);
            new Thread(stderrThread).start();
        }
    }

    public void close() {
        try {
            if (bisInInner != null) {
                bisInInner.close();
                bisInInner = null;
            }
        } catch (IOException e) {
        }
        
        try{
            if (bosOutOuter != null) {
                bosOutOuter.flush();
                bosOutOuter.close();
                bosOutOuter = null;
            }
        } catch (IOException e) {
        }

        try {
            if (bisInOuter != null) {
                bisInOuter.close();
                bisInOuter = null;
            }
        } catch (IOException e) {
        }

        try {
            if (bosOutOuter != null) {
                bosOutOuter.flush();
                bosOutOuter.close();
                bosOutOuter = null;
            }
        } catch (IOException e) {
        }

        try {
            if (bisErrOuter != null) {
                bisErrOuter.close();
                bisErrOuter = null;
            }
        } catch (IOException e) {
        }

        try {
            if (bosErrInner != null) {
                bosErrInner.flush();
                bosErrInner.close();
                bosErrInner = null;
            }
        } catch (IOException e) {
        }

        IConsoleManager manager = ConsolePlugin.getDefault()
                .getConsoleManager();
        manager.removeConsoles(new IConsole[] { this });
    }
}
This entry was posted in eclispe and tagged , , , . Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *