Proceso Java con flujos de entrada / salida concurrentes
Estoy tratando de crear una especie de consola / terminal que permita al usuario ingresar una cadena, que luego se convierte en un proceso y los resultados se imprimen. Al igual que una consola normal. Pero tengo problemas para administrar los flujos de entrada / salida. He investigadoeste hilo, pero esa solución lamentablemente no se aplica a mi problema.
Junto con los comandos estándar como "ipconfig" y "cmd.exe", necesito poder ejecutar un script y usar el mismo flujo de entrada para pasar algunos argumentos, si el script solicita información.
Por ejemplo, después de ejecutar un script "python pyScript.py", debería poder pasar más datos al script si lo solicita (ejemplo: raw_input), mientras también imprimo el resultado del script. El comportamiento básico que esperarías de una terminal.
Lo que tengo hasta ahora:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextPane;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
public class Console extends JFrame{
JTextPane inPane, outPane;
InputStream inStream, inErrStream;
OutputStream outStream;
public Console(){
super("Console");
setPreferredSize(new Dimension(500, 600));
setLocationByPlatform(true);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// GUI
outPane = new JTextPane();
outPane.setEditable(false);
outPane.setBackground(new Color(20, 20, 20));
outPane.setForeground(Color.white);
inPane = new JTextPane();
inPane.setBackground(new Color(40, 40, 40));
inPane.setForeground(Color.white);
inPane.setCaretColor(Color.white);
JPanel panel = new JPanel(new BorderLayout());
panel.add(outPane, BorderLayout.CENTER);
panel.add(inPane, BorderLayout.SOUTH);
JScrollPane scrollPanel = new JScrollPane(panel);
getContentPane().add(scrollPanel);
// LISTENER
inPane.addKeyListener(new KeyListener(){
@Override
public void keyPressed(KeyEvent e){
if(e.getKeyCode() == KeyEvent.VK_ENTER){
e.consume();
read(inPane.getText());
}
}
@Override
public void keyTyped(KeyEvent e) {}
@Override
public void keyReleased(KeyEvent e) {}
});
pack();
setVisible(true);
}
private void read(String command){
println(command);
// Write to Process
if (outStream != null) {
System.out.println("Outstream again");
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(outStream));
try {
writer.write(command);
//writer.flush();
//writer.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}
// Execute Command
try {
exec(command);
} catch (IOException e) {}
inPane.setText("");
}
private void exec(String command) throws IOException{
Process pro = Runtime.getRuntime().exec(command, null);
inStream = pro.getInputStream();
inErrStream = pro.getErrorStream();
outStream = pro.getOutputStream();
Thread t1 = new Thread(new Runnable() {
public void run() {
try {
String line = null;
while(true){
BufferedReader in = new BufferedReader(new InputStreamReader(inStream));
while ((line = in.readLine()) != null) {
println(line);
}
BufferedReader inErr = new BufferedReader(new InputStreamReader(inErrStream));
while ((line = inErr.readLine()) != null) {
println(line);
}
Thread.sleep(1000);
}
} catch (Exception e) {
e.printStackTrace();
}
}
});
t1.start();
}
public void println(String line) {
Document doc = outPane.getDocument();
try {
doc.insertString(doc.getLength(), line + "\n", null);
} catch (BadLocationException e) {}
}
public static void main(String[] args){
new Console();
}
}
No uso lo mencionadoProcessBuilder
, ya que me gusta diferenciar entre error y flujo normal.
ACTUALIZACIÓN 29.08.2016
Con la ayuda de @ArcticLord hemos logrado lo que se pidió en la pregunta original. Ahora es solo una cuestión de resolver cualquier comportamiento extraño como el proceso sin finalización. La consola tiene un botón "detener" que simplemente llama a pro.destroy (). Pero por alguna razón esto no funciona para procesos que se ejecutan infinitamente, es decir, salidas de spam.
Consola:http://pastebin.com/vyxfPEXC
InputStreamLineBuffer:http://pastebin.com/TzFamwZ1
Código de ejemplo que haceno detener:
public class Infinity{
public static void main(String[] args){
while(true){
System.out.println(".");
}
}
}
Código de ejemplo que se detiene:
import java.util.concurrent.TimeUnit;
public class InfinitySlow{
public static void main(String[] args){
while(true){
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(".");
}
}
}