Execute external program using ProcessBuilder and provide input
I'm trying to execute an external program with Java using ProcessBuilder
, but it expects input from the user.
To be more specific, the program is PGSQL
(Postgres SQL), when it's executed the program prompts the user for a password. The only way to bypass that is to save a file in the user home containing the passwords, I'm trying to avoid that, so I want to execute the program from Java and send the password using the process' output str开发者_开发问答eam.
The code works fine when the program doesn't expect any user input, but when I delete the password file from the user home, the program hangs. I see that it's being executed, but nothing happens. If I debug it, it reaches the while and then nothing happens until I kill the process.
This is the code, any help will be greatly appreciated.
@Test
public void testSQLExecution() throws Exception {
String path = "C:/tmp";
List<String> commandList = new ArrayList<String>();
commandList.add("psql");
commandList.add("-f");
commandList.add("test.sql");
commandList.add("-h");
commandList.add(HOST);
commandList.add("-p");
commandList.add(PORT);
commandList.add("-U");
commandList.add(DATABASE);
commandList.add(SCHEMA);
ProcessBuilder processBuilder = new ProcessBuilder(commandList);
processBuilder.directory(new File(path));
processBuilder.redirectErrorStream(true);
Process p = processBuilder.start();
String line;
BufferedReader input = new BufferedReader(new InputStreamReader(p
.getInputStream()));
BufferedWriter out = new BufferedWriter(new OutputStreamWriter(p
.getOutputStream()));
out.write("password");
out.newLine();
out.flush();
out.close();
// When this line is reached, the execution halts.
while (input.ready() && (line = input.readLine()) != null) {
System.out.println(line);
}
if (p.waitFor() != 0) {
Assert.fail("The process did not run succesfully.");
}
input.close();
}
Thanks a lot.
I believe the prompt is going to STDERR, not STDOUT, so you'll have to open a stream connected to that and read there. When you try to read from STDOUT your code hangs waiting for output that will never arrive.
EDIT: I see you have redirected the error stream in the ProcessBuilder.
One other possibility is that the BufferedReader is waiting for a newline to finish reading, and the prompt does not end with a newline.
Been a long time, but got exactly the same problem. Here is a SSCCE that should work:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Toto
{
private static Logger logger = LoggerFactory.getLogger(Toto.class);
static class Params
{
public String getUserName() {
return "PUT USERNAME HERE";
}
public String getHost() {
return "PUT HOST HERE";
}
public String getDbName() {
return "PUT DBNAME HERE";
}
public char[] getPassword() {
return new char[]{'p','a','s','s','w','o','r','d'};
}
public String getSqlFile() {
return "PUT SQL COMMAND FILE HERE";
}
}
public static void main(String[] args)
{
Params params = new Params();
try {
final String userName = params.getUserName();
final String host = params.getHost();
final String dbName = params.getDbName();
final char[] pass = params.getPassword();
if (userName == null || host == null || dbName == null || pass == null) {
logger.error("Missing the following info to execute the SQL command file: {} {} {} {}" , userName == null ? "username": ""
, host == null ? "host" : ""
, dbName == null ? "database": ""
, pass == null ? "password": "");
return;
}
List<String> sb = new ArrayList<String>();
sb.add("psql");
sb.add("-h");
sb.add(host);
sb.add("-U");
sb.add(userName);
sb.add("-d");
sb.add(dbName);
sb.add("-f");
sb.add(params.getSqlFile());
// sb.add("-W"); // force password prompt
logger.debug("Executing the following command: {}", sb.toString());
ProcessBuilder pb = new ProcessBuilder(sb);
final Process p = pb.start();
final BufferedReader stdinReader = new BufferedReader(new InputStreamReader(p.getInputStream()));
final BufferedReader stderrReader = new BufferedReader(new InputStreamReader(p.getErrorStream()));
new Thread(new Runnable()
{
@Override
public void run()
{
try
{
OutputStreamWriter s = new OutputStreamWriter(p.getOutputStream());
s.write(pass);
s.write(System.getProperty("line.separator"));
s.flush();
System.out.println("Pass written");
}
catch(IOException e)
{
logger.error("Exception raised in the thread writting password to psql", e);
}
}
}).start();
new Thread(new Runnable()
{
@Override
public void run()
{
try
{
String s;
while (( s=stdinReader.readLine()) != null) {
logger.debug("psql [STDOUT]: {}", s);
}
}
catch(IOException e)
{
logger.error("Exception raised in thread displaying stdout of psql", e);
}
}
}).start();
new Thread(new Runnable()
{
@Override
public void run()
{
try
{
String s;
while (( s=stderrReader.readLine()) != null) {
logger.error("psql [STDERR]: {}", s);
}
}
catch(IOException e)
{
logger.error("Exception raised in thread displaying stderr of psql", e);
}
}
}).start();
int returnVal = p.waitFor();
logger.debug("Process ended with return val {} ", returnVal);
}
catch (Exception e) {
logger.error("Exception raised while executing the results on server", e);
}
}
}
精彩评论