/* * Copyright (C) 2015 drrb * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package com.github.drrb.rust.netbeans.commandrunner; import com.github.drrb.rust.netbeans.configuration.Os; import com.github.drrb.rust.netbeans.util.IoColorLines; import com.github.drrb.rust.netbeans.util.Pipe; import com.github.drrb.rust.netbeans.util.Untested; import java.awt.event.ActionEvent; import java.io.File; import java.io.IOException; import javax.swing.AbstractAction; import org.netbeans.api.progress.ProgressHandle; import org.netbeans.api.progress.ProgressHandleFactory; import org.openide.LifecycleManager; import org.openide.execution.ExecutionEngine; import org.openide.execution.ExecutorTask; import org.openide.util.Cancellable; import org.openide.util.Exceptions; import org.openide.util.RequestProcessor; import org.openide.util.Task; import org.openide.util.TaskListener; import org.openide.windows.IOProvider; import org.openide.windows.InputOutput; @Untested(excuses = "Lots of UI stuff") public class CommandRunner { private static final RequestProcessor EXECUTOR = new RequestProcessor("Command runner", 12); public static CommandRunner get(String name) { return new CommandRunner(name); } private final String name; private final Shell shell; public CommandRunner(String name) { this(name, Os.getCurrent().shell()); } CommandRunner(String name, Shell shell) { this.name = name; this.shell = shell; } public CommandFuture run(String commandLine, File workingDir) { LifecycleManager.getDefault().saveAll(); InputOutput io = IOProvider.get(name).getIO(name, false); IoColorLines.printDebug(io, commandLine); ProcessBuilder processBuilder = shell.createProcess(commandLine).directory(workingDir); WatchingProcessRunner processRunner = new WatchingProcessRunner(processBuilder, io); ExecutorTask task = ExecutionEngine.getDefault().execute(name, processRunner, io); task.addTaskListener(new CleanUpStreamsWhenFinished(io)); return processRunner.future; } private class WatchingProcessRunner implements Runnable { private final ProcessBuilder processBuilder; private final InputOutput io; final CommandFuture future = new CommandFuture(); WatchingProcessRunner(ProcessBuilder processBuilder, InputOutput io) { this.processBuilder = processBuilder; this.io = io; } @Override public void run() { try { Process process = processBuilder.start(); watch(process); } catch (IOException ex) { Exceptions.printStackTrace(ex); } } public void watch(Process process) { ProgressHandle progressHandle = ProgressHandleFactory.createHandle(name, new KillOnCancel(process), new SelectOnClick(io)); progressHandle.start(); try { //TODO: wait for these? EXECUTOR.post(Pipe.between(future.wrap(process.getInputStream()), io.getOut())); EXECUTOR.post(Pipe.between(process.getErrorStream(), io.getErr())); EXECUTOR.post(Pipe.between(io.getIn(), process.getOutputStream())); process.waitFor(); } catch (InterruptedException ex) { process.destroy(); } finally { progressHandle.finish(); } } private class KillOnCancel implements Cancellable { private final Process process; KillOnCancel(Process process) { this.process = process; } @Override public boolean cancel() { process.destroy(); return true; } } private class SelectOnClick extends AbstractAction { private final InputOutput io; private SelectOnClick(InputOutput io) { this.io = io; } @Override public void actionPerformed(ActionEvent e) { io.select(); } } } private class CleanUpStreamsWhenFinished implements TaskListener { private final InputOutput io; CleanUpStreamsWhenFinished(InputOutput io) { this.io = io; } @Override public void taskFinished(Task task) { io.getOut().append("\n"); io.getOut().close(); io.getErr().close(); try { io.getIn().close(); } catch (IOException ex) { Exceptions.printStackTrace(ex); } } } }