9 Replies Latest reply on Jan 10, 2012 4:37 PM by 909372

    Interacting with Powershell from Java

    909372
      Hi,

      I'm trying to run a Java application which creates a new powershell process on startup and then later on interacts with it multiple times. Calling powershell.exe and have it execute a single command and return the output works fine for me. The problem arises if I don't want the powershell process to immediately finish/exit but to stay open so I can write to its outputStream and receive results back from the inputStream.

      ---------------------
      String input = "dir";

      String[] commandList = {"powershell.exe", "-Command", "dir"};

      ProcessBuilder pb = new ProcessBuilder(commandList);

      Process p = pb.start();

      if(input != null) { 
      PrintWriter writer = new PrintWriter(new OutputStreamWriter(new BufferedOutputStream(p.getOutputStream())), true);
      writer.println(input);
      writer.flush();
      writer.close();

      }

      //p.getOutputStream().close();

      Gobbler outGobbler = new Gobbler(p.getInputStream());
      Gobbler errGobbler = new Gobbler(p.getErrorStream());
      Thread outThread = new Thread(outGobbler);
      Thread errThread = new Thread(errGobbler);
      outThread.start();
      errThread.start();

      System.out.println("Waiting for the Gobbler threads to join...");

      outThread.join();
      errThread.join();

      System.out.println("Waiting for the process to exit...");

      int exitVal = p.waitFor();
      System.out.println("\n****************************");
      System.out.println("Command: " + "cmd.exe /c dir");
      System.out.println("Exit Value = " + exitVal);
      List<String> output = outGobbler.getOuput();
      input = "";
      for(String o: output) { 
      input += o;
      }

      System.out.println("Final Output:");
      System.out.println(input);
      ----------------------

      This code returns the result of the "dir" command from a powershell - fine. But as you can see, I'm trying to run a second "dir" command using
      ----------------------------
      PrintWriter writer = new PrintWriter(new OutputStreamWriter(new BufferedOutputStream(p.getOutputStream())), true);
      writer.println(input);
      writer.flush();
      ---------------------
      This has no effect whatsoever - no second dir output is shown when I run my code. I've also experimented with a powershell.exe option to open the powershell but not close it immediately:
      String[] commandList = {"powershell.exe", "-NoExit", "-Command", "dir"};

      But then my code hangs, meaning the Gobbler's who consume the process's inputStream don't read anything - strangely enough: they don't even read the first line - there must be at least some output....

      I've also tried to close the process's outputStream after writing the second "dir" command to it - didn't change anything.

      But when I initially call the cmd.exe using the /k (keep open) switch:
      String[] commandList = {"cmd.exe", "/k", "dir"};

      I can then still write to that outputstream and invoke the second "dir" command and get the output of both "dir" commands from the inputstream fo that process.

      Any help is highly appreciated.
      Thanks
      Kurt
        • 1. Re: Interacting with Powershell from Java
          sabre150
          I misunderstood the problem.
          • 2. Re: Interacting with Powershell from Java
            452196
            Try :

            Start gobblers before writing anthing.

            User one Writer stack etc. for all commands, and don't close it until all commands are written (closing the writer will close the output stream and, hence, tell the PowerShell that you've finished with it).
            • 3. Re: Interacting with Powershell from Java
              909372
              @sabre150: The challenge is to start a powershell from Java when my application starts and then keep that powershell open as long as my application runs (it's a server so it's supposed to always run). Then, when the client calls the server for some data, the server should write some commands to that existing powershell process and read the corresponding results and push them back to the client (also in Java).

              Calling a powershell.exe process once, initially passing a long some commands and retrieving the result works fine. But when I want to send additional commands to that powershell and read the output it won't work.
              • 4. Re: Interacting with Powershell from Java
                909372
                @malcolmmc: Not sure I've understood your advice correctly. What I've tested so far:
                1. I've put the Gobblers (consumers) higher up in the code as you can see at the bottom of this reply -> result: no improvement
                2. I've used the ProcessBuilder to create this process and redirected the erroStream to the inputStream - so I only had to use one Gobbler process for both streams -> result: no improvement
                3. I did not close the bufferedWriter in my code -> result: no improvement

                It seems that my code only works half the way when I close the outputStream - if I don't do that, I assume, the powershell process is blocking/waiting for more commands from that stream before processing and finally providing the results on the inpuStream. But still, if I close the outputStream I only get the result from the inital "dir" command (sent along to the process at the time it is initiated) - any additional commands written to the process's input stream (=outputStream) doesn't seem to be read and processed by the powershell process.

                Any other ideas?

                --------------------------

                String[] cmds = {"powershell.exe", "dir"};
                Runtime runtime = Runtime.getRuntime();
                Process p = runtime.exec(cmds);
                          
                Gobbler outGobbler = new Gobbler(p.getInputStream());
                Gobbler errGobbler = new Gobbler(p.getErrorStream());
                Thread outThread = new Thread(outGobbler);
                Thread errThread = new Thread(errGobbler);
                outThread.start();
                errThread.start();

                String input = "dir";
                if(input != null) {
                     PrintWriter writer = new PrintWriter(new OutputStreamWriter(new BufferedOutputStream(p.getOutputStream())), true);
                     writer.println(input);
                     writer.flush();
                     //writer.close();

                }

                //p.getOutputStream().close();
                • 5. Re: Interacting with Powershell from Java
                  kordirko
                  Have you tried to start powershell with arguments: -NoExit -Command - ?

                  Type powershell -help and read what it says:

                  >
                  <pre>-NoExit
                  Does not exit after running startup commands.
                  -Command
                  Executes the specified commands (and any parameters) as though they were
                  typed at the Windows PowerShell command prompt, <font color="red">and then exits, unless
                  NoExit is specified..</font> The value of Command can be "-", a string. or a
                  script block.
                  <font color="red"> If the value of Command is "-", the command text is read from standard
                  input.</font>
                  </pre>
                  • 6. Re: Interacting with Powershell from Java
                    909372
                    yes, I have read the info.

                    When I perform a runtime.exec with "-NoExit" it does what it says: powershell is never exiting and thus my code is hanging. From what I've research so far, I guess, the powershell waits for the outputStream (its inputstream) to be closed until it performs the requested operations. But I can't close this stream since I want to further interact with powershell and, once closed, the stream can never be opened again.

                    When I use the "-Command" option and add some command afterwards (like "dir") and I close the outputStream everything works fine meaning my code doesn't hang, the powershell process will be closed and I see the result from the "dir" command - but that doesn't help :(

                    thanks again
                    Kurt
                    • 7. Re: Interacting with Powershell from Java
                      909372
                      -Command
                      Executes the specified commands (and any parameters) as though they were
                      typed at the Windows PowerShell command prompt, <font color="red">and then exits, unless
                      NoExit is specified..</font> The value of Command can be "-", a string. or a
                      script block.
                      <font color="red"> If the value of Command is "-", the command text is read from standard
                      input.</font>
                      </pre>>
                      Let me be more specific on the second suggestion:
                      This was actually a really good hint (thanks!) - using "powershell.exe", "-Command", "-" as the process's start parameters actually gives me the possiblity to write to the process's inputStream again and again as often as I want - nice!
                      BUT: my Gobblers only read the output of all my commands when I perform a "p.getOutputStream().close();" - but once I do this, I can never re-open the outputStream, correct!? Or maybe there is another way of reading the inputStream - I use a bufferedReader which seems to only start reading when the outputStream closese...not sure why.
                      • 8. Re: Interacting with Powershell from Java
                        kordirko
                        user4491593 wrote:
                        BUT: my Gobblers only read the output of all my commands
                        Then why don't change your Gobbler code ? ;)
                        Test this, it's ugly and needs improvemens, but by now works fine on linux and windows:
                        public class Gobbler implements Runnable {
                        
                            private PrintStream out;
                            private String message;
                        
                            private BufferedReader reader;
                        
                            public Gobbler(InputStream inputStream, PrintStream out) {
                                this.reader = new BufferedReader(new InputStreamReader(inputStream));
                                       this.out = out;
                                this.message = ( null != message ) ? message : "";
                            }
                        
                            public void run() {
                                String line;
                        
                                try {
                                    while (null != (line = this.reader.readLine())) {
                                        out.println(message + line);
                                    }
                                    this.reader.close();
                                } catch (IOException e) {
                                    System.err.println("ERROR: " + e.getMessage());
                                }
                            }
                        }
                        
                        
                        public class PowerConsole {
                        
                            private ProcessBuilder pb;
                            Process p;
                            boolean closed = false;
                            PrintWriter writer;
                        
                            PowerConsole(String[] commandList) {
                                pb = new ProcessBuilder(commandList);
                                try {
                                    p = pb.start();
                                } catch (IOException ex) {
                                    throw new RuntimeException("Cannot execute PowerShell.exe", ex);
                                }
                                writer = new PrintWriter(new OutputStreamWriter(new BufferedOutputStream(p.getOutputStream())), true);
                                Gobbler outGobbler = new Gobbler(p.getInputStream(), System.out);
                                Gobbler errGobbler = new Gobbler(p.getErrorStream(), System.out);
                                Thread outThread = new Thread(outGobbler);
                                Thread errThread = new Thread(errGobbler);
                                outThread.start();
                                errThread.start();
                            }
                        
                            public void execute(String command) {
                                if (!closed) {
                                    writer.println(command);
                                    writer.flush();
                                } else {
                                    throw new IllegalStateException("Power console has ben closed.");
                                }
                            }
                        
                            public void close() {
                                try {
                                    execute("exit");
                                    p.waitFor();
                                } catch (InterruptedException ex) {
                                }
                            }
                        
                            public static void main(String[] args) throws IOException, InterruptedException {
                                /*   PowerConsole pc = new PowerConsole(new String[]{"/bin/bash"});
                                
                                PowerConsole pc = new PowerConsole(new String[]{"/bin/bash"});
                                pc.execute("pwd");
                                pc.execute("ls");
                                pc.execute("cd /");
                                pc.execute("ls -l");
                                pc.execute("cd ~");
                                pc.execute("find . -name 'test.*' -print");
                                pc.close();
                                 */
                                //      PowerConsole pc = new PowerConsole(new String[]{"cmd.exe"});
                                PowerConsole pc = new PowerConsole(new String[]{"powershell.exe", "-NoExit", "-Command", "-"});
                                System.out.println("========== Executing dir");
                                pc.execute("dir"); 
                                System.out.println("========== Executing cd\\");
                                pc.execute("cd \\"); Thread.sleep(2000);
                                System.out.println("========== Executing dir");
                                pc.execute("dir"); Thread.sleep(2000);
                                System.out.println("========== Executing cd \\temp");
                                pc.execute("cd \\temp"); Thread.sleep(2000);
                                System.out.println("========== Executing dir");
                                pc.execute("dir"); Thread.sleep(2000);
                                System.out.println("========== Executing cd \\bubba");
                                pc.execute("cd \\bubba"); Thread.sleep(2000);
                                System.out.println("========== Exiting .... bye.");
                                pc.close();
                            }
                        }
                        I tested this and there is still a little problem -look at the test below.
                        It seems that when thecommand
                        executed in the powershell prints only a one ot two lines,
                        powershell doesn't flush the output stream
                        .... but this rather problem of powershell, not the java code
                        I have not a clue how to force powershell to flush
                        it's output stream after each command.
                        C:\temp>java -jar PowerShell.jar
                        ========== Executing dir
                        
                        
                            Directory: Microsoft.PowerShell.Core\FileSystem::C:\temp
                        
                        
                        Mode                LastWriteTime     Length Name
                        ----                -------------     ------ ----
                        -a---        2012-01-09     01:16       5290 PowerShell.jar
                        
                        
                        ========== Executing cd\
                        ========== Executing dir
                        
                        
                            Directory: Microsoft.PowerShell.Core\FileSystem::C:\
                        
                        
                        Mode                LastWriteTime     Length Name
                        ----                -------------     ------ ----
                        d----        2012-01-08     02:56            61587b977687a6e22fbe
                        d----        2011-12-14     03:19            Documents and Settings
                        d----        2011-12-15     00:05            oraclexe
                        d-r--        2012-01-08     03:44            Program Files
                        d----        2012-01-05     19:59            sqldeveloper
                        d----        2012-01-09     01:15            temp
                        d----        2012-01-09     01:13            WINDOWS
                        -a---        2011-12-14     03:12          0 AUTOEXEC.BAT
                        -a---        2011-12-14     03:12          0 CONFIG.SYS
                        
                        
                        ========== Executing cd \temp
                        ========== Executing dir
                        
                        
                            Directory: Microsoft.PowerShell.Core\FileSystem::C:\temp
                        
                        
                        Mode                LastWriteTime     Length Name
                        ----                -------------     ------ ----
                        -a---        2012-01-09     01:16       5290 PowerShell.jar
                        
                        
                        ========== Executing cd \bubba
                        Set-Location : Cannot find path 'C:\bubba' because it does not exist.
                        At line:1 char:3
                        + cd  <<<< \bubba
                        ========== Exiting .... bye.
                        
                        C:\temp>
                        • 9. Re: Interacting with Powershell from Java
                          909372
                          Hi kordirko,

                          that's it! you've found the solution - THANKS a lot!

                          I incorporated this into my code and at least when running this on a Windows server 2008 R2 the Powershell seems to flush all its output and the Gobblers read it - I haven't done full testing yet but I will close this thread for the moment as my problem seems solved.

                          thanks again, really great support!
                          Kurt