views:

266

answers:

4

I'm trying to enter some value in external application using Java.

Java application looks like this:

Runtime runtime = Runtime.getRuntime();
// ... str build ...
proc = runtime.exec(str);
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(proc.getOutputStream()));
bw.write(value);
bw.flush();
bw.close();
if (proc.waitFor() != 0) 
    // error msg
// the end

Application hangs at waitFor method.

External application looks like this:

welcome banner

please enter 8 character input:

Welcome banner is printed using printf and input is taken with SetConsoleMode/ReadConsoleInput. ReadConsoleInput reads one char and they are masked with * character.

Help

+2  A: 

you can use:

proc.getOutputStream().write("some date".getBytes())

keep in mind that you HAVE to read everything the app send to stdout and stderr, else it might get stuck writing there. I use a generic class to read it in a different thread. usage is like:

InputStreamSucker inSucker = new InputStreamSucker(proc.getInputStream());
InputStreamSucker errSucker = new InputStreamSucker(proc.getErrorStream());
proc.waitFor();
int exit = process.exitValue();
inSucker.join();
errSucker.join();

InputStreamSucker code is here:

public class InputStreamSucker extends Thread
{
    static Logger logger = Logger.getLogger(InputStreamSucker.class);

    private final BufferedInputStream m_in;

    private final ByteArrayOutputStream m_out;

    private final File m_outFile;

    public InputStreamSucker(InputStream in) throws FileNotFoundException
    {
     this(in, null);
    }


    public InputStreamSucker(InputStream in, File outFile) throws FileNotFoundException
    {
     m_in = new BufferedInputStream(in, 4096);
     m_outFile = outFile;
     m_out = new ByteArrayOutputStream();
     start();
    }

    @Override
    public void run()
    {
     try
     {
      int c;
      while ((c = m_in.read()) != -1)
      {
       m_out.write(c);
      }
     }
     catch (IOException e)
     {
      logger.error("Error pumping stream", e);
     }
     finally
     {
      if (m_in != null)
      {
       try
       {
        m_in.close();
       } 
       catch (IOException e)
       {
       }
      }

      try
      {
       m_out.close();
      }
      catch (IOException e)
      {
       logger.error("Error closing out stream", e);
      }

      if (m_outFile != null)
      {
       byte data[] = m_out.toByteArray();
       if (data.length > 0)
       {
        FileOutputStream fo = null;
        try
        {
         fo = new FileOutputStream(m_outFile);
         fo.write(data);
        }
        catch (IOException e)
        {
         logger.error("Error writing " + m_outFile);
        }
        finally
        {
         try
         {
          if (fo != null) fo.close();
         }
         catch (IOException e)
         {
          logger.error("Error closing " + m_outFile);
         }
        }
       }
      }
     }
    }

    public String getOutput()
    {
     return new String(m_out.toByteArray());
    }
}
Omry
Omry, does the order matter? I should first read input/error stream and then write?
Omry, thanks for the code, I've tried it, but it still stucks at waitFor.
well, if your process is waiting for input, you should provide it.you can hack my code to print to System.out, so you will see what's happening with your process. you can also just tail the log file.make sure you use proc.getOutputStream().write() to send data to your process.
Omry
order should not matter.
Omry
Thx Omry, I already did System.out in your code and while output is read, at least to point where the external app is asking me to enter the value. Btw, when I type value in application they are displayed using * character, but I dont see then when I write value using Java - that probably means the write wasn't successful? :(
Java successfully reads this: welcome banner please enter 8 character input:At this point Java writes input and they should be displayed with * character :-(
Maybe should I write one byte at the time? Because of ReadConsoleInput implementation in external application?
oh.try to call the flush() method of the output stream after write the data.
Omry
I see you already do it. if you program expects an enter send a newline (\n)
Omry
the timing of sending the output MAY matter if your program is behaving strangely.
Omry
A: 

Still not working :-(

I'm trying to recreate external application using disassembly code and this is pretty close to it ... Imagine this as a external C/C++ application in which I'm trying to enter value using Java:

HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE); 
DWORD cNumRead;
INPUT_RECORD irBuffer[1]; 
int x = 0;

printf("\nwelcome banner\n\nplease enter 8 characters: ");

SetConsoleMode(hStdin, 0);
do {
 ReadConsoleInput(hStdin, irBuffer, 1, &cNumRead);
 for (int i = 0; i < 8; i++) {
  switch (irBuffer[i].EventType) {
   case KEY_EVENT:
    if (irBuffer[i].Event.KeyEvent.bKeyDown) {
     printf("*");
     fflush(stdout);
     x++;
    }
    break;
   default:
    break;
  }
 }
} while (x < 8);

printf("\nsuccessfully finished\n");

return 0;
A: 

Got the answer! The trick is to use WriteConsoleInput() API because program expects keyboard event, not text ... That's why the waitFor() waited forever! :)

A: 

Hey johnhy, I think that I'm having the same problem as you, could you please explain a little further what you mean about using WriteConsoleInput() API? I've looked for documentation about it but I don't see any API for Java.

Here's the question I've posted regarding my problem.

http://stackoverflow.com/questions/3240704/execute-external-program-using-processbuilder-and-provide-input

Thanks a lot, and sorry for asking more questions instead of giving an answer. Regards.

Harry Potel