разработка оболочки для утилит командной строки - PullRequest
2 голосов
/ 17 марта 2010

я пытаюсь придумать дизайн для оболочки для использования при вызове утилит командной строки в Java. проблема с runtime.exec () заключается в том, что вам нужно продолжать чтение из потоков процесса и ошибок, иначе он зависнет при заполнении своих буферов. это привело меня к следующему дизайну:

public class CommandLineInterface {
    private final Thread stdOutThread;
    private final Thread stdErrThread;
    private final OutputStreamWriter stdin;
    private final History history;


    public CommandLineInterface(String command) throws IOException {
        this.history = new History();
        this.history.addEntry(new HistoryEntry(EntryTypeEnum.INPUT, command));
        Process process = Runtime.getRuntime().exec(command);
        stdin = new OutputStreamWriter(process.getOutputStream());
        stdOutThread = new Thread(new Leech(process.getInputStream(), history, EntryTypeEnum.OUTPUT));
        stdOutThread.setDaemon(true);
        stdOutThread.start();
        stdErrThread = new Thread(new Leech(process.getErrorStream(), history, EntryTypeEnum.ERROR));
        stdErrThread.setDaemon(true);
        stdErrThread.start();
    }

    public void write(String input) throws IOException {
        this.history.addEntry(new HistoryEntry(EntryTypeEnum.INPUT, input));
        stdin.write(input);
        stdin.write("\n");
        stdin.flush();
    }
}

И

public class Leech implements Runnable{
    private final InputStream stream;
    private final History history;
    private final EntryTypeEnum type;
    private volatile boolean alive = true;


    public Leech(InputStream stream, History history, EntryTypeEnum type) {
        this.stream = stream;
        this.history = history;
        this.type = type;
    }

    public void run() {
        BufferedReader reader = new BufferedReader(new InputStreamReader(stream));
        String line;
        try {
            while(alive) {
                line = reader.readLine();
                if (line==null) break;
                history.addEntry(new HistoryEntry(type, line));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Моя проблема связана с классом Leech (который используется для «вытекания» из потока процессов и ошибок и подачи их в историю - что действует как файл журнала) - с одной стороны, чтение целых строк приятно и легко (и что я сейчас делаю), но это означает, что я пропускаю последнюю строку (обычно строку подсказки). я вижу только строку подсказки при выполнении следующей команды (потому что до этой точки нет разрыва строки). с другой стороны, если я сам читаю символы, как я могу сказать, когда процесс «завершен»? (либо завершено, либо ожидает ввода) кто-нибудь пробовал что-то вроде ожидания 100 миллисекунд с момента последнего вывода из процесса и объявления его «выполнено»?

Какие-нибудь лучшие идеи о том, как я могу реализовать хорошую оболочку для таких вещей, как runtime.exec ("cmd.exe")?

Ответы [ 3 ]

2 голосов
/ 23 марта 2010

Использовать PlexusUtils он используется Apache Maven 2 для выполнения всех внешних процессов.

1 голос
/ 21 марта 2010

Я сам искал то же самое и нашел порт Java Expect, названный ExpectJ . Я еще не пробовал, но выглядит многообещающе

0 голосов
/ 23 марта 2010

Я бы прочитал входные данные в потоке, а затем записал бы их в ByteArrayOutputStream. Массив байтов будет продолжать расти, пока не останется доступных для чтения байтов. На этом этапе вы сбросите данные в историю, преобразовав байтовый массив в строку и разделив его на платформе line.separator. Затем вы можете перебирать строки, чтобы добавить записи истории. Затем ByteArrayOutputStream сбрасывается, и цикл while блокируется до тех пор, пока не будет больше данных или пока не будет достигнут конец потока (вероятно, потому что процесс завершен).

public void run() {
    ByteArrayOutputStream out = new ByteArrayOutputStream();

    int bite;
    try {
        while((bite = stream.read()) != -1) {
            out.write(bite);

            if (stream.available() == 0) {
                String string = new String(out.toByteArray());
                for (String line : string.split(
                        System.getProperty("line.separator"))) {
                    history.addEntry(new HistoryEntry(type, line));
                }

                out.reset();
            }
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
}

Это гарантирует, что вы выберете последнюю строку ввода, и решит вашу проблему, зная, когда заканчивается поток.

...