Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Command line terminal executing on process and input interaction from this process [duplicate]

Tags:

java

process

Basically, I have a process which runs when I press a button on my java application. And this process executes a command to the terminal of the OS. But sometimes this command needs to have an interaction with the user. And I would like to know if this was possible to have the interaction from the process to the user when needed?

My code:

File marsSimulator = new File("resources/mars_simulator/Mars4_5.jar");
if(marsSimulator.exists() && temp.exists()){
   String res="";
   try {
     Process p = Runtime.getRuntime().exec(new String[]{"java","-jar",marsSimulator.getAbsolutePath(),tempAssembly.getAbsolutePath()});
     p.waitFor();

     InputStream is = p.getInputStream();

     byte b[] = new byte[is.available()];
     is.read(b, 0, b.length); // probably try b.length-1 or -2 to remove "new-line(s)"

     res = new String(b);

   } catch (Exception ex) {
     ex.printStackTrace();
   }
}

Also, I forgot to say that the application is made with SWING and that the output of the process is shown onto a TextArea... Should I change anything ?

Notice that the process blocks when there is an interaction with the user. If there isn't, the process doesn't block !

What do I need to do in this case (which I don't know how to do it ) ?

  1. When the process needs the interaction. I need to know when the process wants some interaction.
  2. I need to get the output generated of the process interactively (line by line).

P.S.: For people who wanna understand the process line, I am using the Mars Simulator (http://courses.missouristate.edu/KenVollmar/MARS/) and I am sending the jar application into a process with a mips assembly code associated.


This next pieces of code is working with my project

Hope it will help for the next adventurers!

And thank you to Nicolas Filotto for helping me.

My class ObservableStream:

class ObservableStream extends Observable {
  private final Queue<String> lines = new ConcurrentLinkedQueue<>();

  public void addLine(String line) {
    lines.add(line);
    setChanged();
    notifyObservers();
  }

  public String nextLine() {
    return lines.poll();
  }

  public String getLine(){return lines.peek();}
}

And the other part of the code:

Process p = Runtime.getRuntime().exec(new String[]{"java","-jar",marsSimulator.getAbsolutePath(),tempAssembly.getAbsolutePath()});

//This code does the interaction from the process with the GUI ! Implied, input interaction+output interaction from the process
ObservableStream out = new ObservableStream();
// Observer that simply sends to my external process line by line what we put in
// the variable output
PrintWriter writer = new PrintWriter(p.getOutputStream(), true);
out.addObserver(
  (o, arg) -> {
    ObservableStream stream = (ObservableStream) o;
    String line;
    while ((line = stream.nextLine()) != null) {
      writer.println(line);
    }
  }
);

ObservableStream input = new ObservableStream();
input.addObserver(
  (o, arg) -> {
    ObservableStream stream = (ObservableStream) o;
    String line;
    while ((line = stream.nextLine()) != null) {
      outputTextArea.appendText(line+"\n");
    }
  }
);

// The thread that reads the standard output stream of the external process
// and put the lines into my variable input
new Thread(
  () -> {
    try (BufferedReader reader = new BufferedReader(
      new InputStreamReader(p.getInputStream()))
    ) {
       String line;
       while ((line = reader.readLine()) != null) {
         input.addLine(line);
      }
    } catch (IOException e1) {
      e1.printStackTrace();
  }
 }
).start();


new Thread(
  ()->{
    while(p.isAlive()){
      String res = input.getLine();
      if(res!=null && res.equals("Enter integer value:")) {
        boolean integerIsRequested=true;
        Thread t=null;
        while(integerIsRequested){
          if(t==null) {
            t = new Thread(new Runnable() {
              public void run() {
                String test1 = JOptionPane.showInputDialog("Enter Integer value:");
                while(!test1.matches("^\\d+$")){
                  test1 = JOptionPane.showInputDialog("Error: Not a valid Integer.\nEnter a correct Integer value:");
                }
                Integer i = Integer.valueOf(test1);

                if (i != null) {
                  out.addLine(test1);
                }
              }
            });
            t.start();

          }
          if(!t.isAlive()){
            integerIsRequested=false;
          }
        }
      }
    }
    outputTextArea.appendText("Program executed\n");
  }
).start();

By the way, this post is unique Jarrod ;)

like image 855
Damiii Avatar asked May 14 '16 00:05

Damiii


People also ask

Which of the following are the commands which accept data from standard input manipulate it and write the results to standard output?

The tee command, used with a pipe, reads standard input, then writes the output of a program to standard output and simultaneously copies it into the specified file or files.

What terminal command can you use to look up options and arguments about a command?

Note: To find out exactly what options each command has available, you can look at its man page. This is done by typing the man command, followed by the name of the command you want to look up, for example man ls .

Is command line the same as terminal?

Command line or CLI (command line interface): In plain English once again, it's the space in which you enter the commands the computer will process. This is practically the same as the terminal and in my opinion these terms can be used interchangeably.

How do you clear the screen in terminal?

Clear Terminal via Ctrl+L / Ctrl+Shift+K Shortcut Keyboard shortcuts also work for clearing the terminal, depending on the terminal emulator. An alternative in some terminal emulators is Ctrl + Shift + K . The command provides the same output as Ctrl + L .


1 Answers

To implement such use case I would personally use:

  1. An Observable object to notify my UI when a new line has been provided by the external process
  2. An Observable object to which I add new lines provided by my UI
  3. An Observer of #1 that will refresh the data of my UI
  4. An Observer of #2 that will send the lines provided by my UI to my external process
  5. A Thread that will check if a new line has been provided by my external process and if so it will provide those lines to #1

So as I don't have your full env, I will show you how it will work with mock objects:

First my fake external application that only does an Echo of what he receives:

public class Echo {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        while (true) {
            String line = scanner.nextLine();
            System.out.printf("echo > %s%n", line);
        }
    }
}

If this class receives foo, it will print into the standard output stream echo > foo

Then my Observable class

public class ObservableStream extends Observable {
    private final Queue<String> lines = new ConcurrentLinkedQueue<>();

    public void addLine(String line) {
        lines.add(line);
        setChanged();
        notifyObservers();
    }

    public String nextLine() {
        return lines.poll();
    }
}

NB: The class ObservableStream (as it is implemented so far) is meant to have only one Observer no more which is enough according to your needs. Indeed is only used to decouple your UI from how the data is retrieved or published

Then finally the main code:

Process p = Runtime.getRuntime().exec(
    new String[]{"java", "-cp", "/my/path/to/my/classes", "Echo"}
);
// The Observable object allowing to get the input lines from my external process
ObservableStream input = new ObservableStream();
// A mock observer that simply prints the lines provided by the external process
// but in your case you will update your text area instead
input.addObserver(
    (o, arg) -> {
        ObservableStream stream = (ObservableStream) o;
        String line;
        while ((line = stream.nextLine()) != null) {
            System.out.printf("Line Received from the external process: %s%n", line);
        }
    }
);
// The thread that reads the standard output stream of the external process 
// and put the lines into my variable input
new Thread(
    () -> {
        try (BufferedReader reader = new BufferedReader(
            new InputStreamReader(p.getInputStream()))
        ) {
            String line;
            while ((line = reader.readLine()) != null) {
                input.addLine(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
).start();
// The Observable object allowing to send the input lines to my external process
ObservableStream output = new ObservableStream();
// Observer that simply sends to my external process line by line what we put in
// the variable output
PrintWriter writer = new PrintWriter(p.getOutputStream(), true);
output.addObserver(
    (o, arg) -> {
        ObservableStream stream = (ObservableStream) o;
        String line;
        while ((line = stream.nextLine()) != null) {
            writer.println(line);
        }
    }
);

// A simple scanner used to send new messages to my external process
Scanner scanner = new Scanner(System.in);
while (true) {
    output.addLine(scanner.nextLine());
}

If this code receives foo, it will print into the standard output stream Line Received from the external process: echo > foo

like image 130
Nicolas Filotto Avatar answered Nov 04 '22 12:11

Nicolas Filotto