run interactive command with apache commons exec - java

I want to run an interactive command with apache commons exec. Everything works except that when my command is executed and waits for user input I don't see my input in the console until I press enter which makes it effectively unusable.
This is an example of an interactive program:
public static void main(String[] args) {
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
String line = null;
while (true) {
System.out.print("=> ");
try {
line = in.readLine();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(line);
}
}
Now I want to execute that with apache commons exec like this:
public static void main(String[] args) {
Executor ex = new DefaultExecutor();
ex.setStreamHandler(new PumpStreamHandler(System.out, System.err, System.in));
CommandLine cl = new CommandLine("java");
cl.addArguments("-cp target\\classes foo.bar.Main");
try {
ex.execute(cl);
} catch (ExecuteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
As I said, it basically works, I get the "=>" prompt but when I type something I don't see it until I hit enter. I'm doing this on windows 7 with a cmd prompt.
I'd appreciate any hint on how to achieve the desired behaviour.
Edit: It works as expected on linux. I guess this is an issue with the windows cmd prompt. I'd still like to make this work if at all possible, so I would appreciate any insight into this behaviour on windows.
Edit2: I also tested with msys shell and powershell, both exhibit the same problem.
Edit3: I worked around the issue by launching a seperate cmd prompt. This works, but I still like to understand why.
CommandLine cl = new CommandLine("cmd");
cl.addArguments("/C java -cp target\\classes foo.bar.Main");
thanks
Raoul

I'm not sure exactly what you were expecting to happen here; if the spawned process is designed to wait to read from its input, then it shouldn't be surprising when it does exactly that?
If that's the issue, and your question is "How can I make my program automatically send a newline character to the spawned process' input?", then you'll need to define an OutputStream to write the input to, and get hold of the ExecuteStreamHandler to attach it to the process. Something like the following:
Executor ex = new DefaultExecutor();
// Create an output stream and set it as the process' input
OutputStream out = new ByteArrayOutputStream();
ex.getStreamHandler().setProcessInputStream(out);
...
try
{
ex.execute(cl);
out.write("\n".getBytes()); // TODO use appropriate charset explicitly
...

Using Apache exec org.apache.commons.exec.DefaultExecuteResultHandler you can launch a non-blocking command. And then you can follow the steps #Andrzej mentioned.

Related

Bat file working from terminal not working from Java

I found my command line tool gets stuck in between and needs a enter to process and I found that it’s because of "QUICKEDIT" mode in cmd and we want to disable it to avoid that. So I searched for Java options to disable quick edit mode on my app launch but I got only bat file from here quickedit.bat.
And this bat file works perfect when I run from my command prompt it disable quick edit mode in the current session itself which is the same I want. So I kept that bat file in my folder via installer and run it first on every launch but it’s not turning off the quick edit mode for current session.
I have tried using both process builder and runtime.exec.
Below is my code
ProcessBuilder pb = new ProcessBuilder("cmd", "/c", "quickedit.bat");
File dir = new File(System.getProperty("user.home")+File.separator+"AppData"+File.separator+"Local"+File.separator);
pb.directory(dir);
Process p=null;
try {
p = pb.start();
} catch (IOException e2) {
// TODO Auto-generated catch block
e2.printStackTrace();
}
BufferedReader in = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line;
try {
while ((line = in.readLine()) != null) {
System.out.println(line); // ----Here i get the same output i get when i run the bat file
}
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
BufferedReader inerr = new BufferedReader(new InputStreamReader(p.getErrorStream()));
try {
while ((line = inerr.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
It gives me this:
When I run my bat file directly like this:
But through Java it didn't disable quick edit in my current command prompt whereas it disables at once I run the actual bat file. So can anyone say the reason or how to fix it or any other way to disable it for ever from Java?
Try pb.inheritIO(); before you call its start method. What you seem to have is a hybrid batch/Powershell script that that relies on stderr to determine which of the two it executes so this should require correct processing of stderr.
I didn't look at your bat file the most common issue on this kind of think is that process run from java did'nt share the environnement variable of your local setup nor jvm a quick fix to verify that is :
pb.environment().putAll(System.getenv());
hoping this will work :) then you just have to found which specific environnment variable is missing :)

Trouble sending multiple values to an external process with apache commons-exec

I'm trying to write a little application that would automate the use of an external application which is cisco any connect mobility client. It provides a command line tools that you can use to connect to your VPN.
I want to run this command line tools from my java application using apache commons-exec library and be able to read his output to send needed information.
I already searched on the net to find "how to communicate" with an external application but the only post I found was this article : Trouble providing multiple input to a Command using Apache Commons Exec and extracting output where it just says "hey I found the solutions", but I don't understand how he did it.
When I start the process, I run a function that read the input like this :
Thread T = new Thread() {
public void run() {
String line;
try {
line = processOutput.readLine();
while (line != null) {
System.out.println(line);
if(line.contains("VPN-Password")){
sendMessage(processInput, "1");
}
if(line.contains("Please enter your username and password")){
sendMessage(processInput, "username");
}
line = processOutput.readLine();
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
};
T.start();
the function send message just run a thread to write in the process inputstream then flush it.
Thread T = new Thread() {
public void run() {
try {
os.write((message+"\n").getBytes());
os.flush();
System.out.println("SENT : "+message);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
};
T.start();
As you can see I check the output to send a message to the process depending on it (basicly to answer questions). However, when it comes to the "Please enter...", I got this exception
java.io.IOException: Read end dead
My issue is that I can't find how to "communicate" with the process by reading his output and sending it messages depending on what it tells me.
Can you help me ?
Thanks for reading.

Running a Perl script in Java using ProcessBuilder

I am using ProcessBuilder in Java to run a Perl script. When I run the Perl script while printing the InputStream of the process, the Java program seems to run for the duration of the Perl script. However if I comment out the getOutPut method in main the Java program terminates very fast and the Perl script does not run at all. Why does this occur?
private final static String SCENARIO = "scen";
/**
* #param args
*/
public static void main(String[] args) {
ProcessBuilder pb = new ProcessBuilder("perl", SCENARIO+".pl");
pb.directory(new File("t:/usr/aman/"+SCENARIO));
try {
Process p = pb.start();
getOutput(p.getInputStream(), true);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private static List getOutput(InputStream is, boolean print) {
List output = new ArrayList<String>();
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
String s = null;
try {
while ((s = reader.readLine()) != null) {
output.add(s);
if(print){
System.out.println(s);
}
}
is.close();
} catch (IOException e) {
// TODO Auto-generated catch block
//e.printStackTrace();
return null;
}
return output;
}
Likely the OS's output stream buffer for your PERL script process gets filled because nothing is emptying this buffer, and this will kill the process. You need to gobble the output stream for this reason which is what your getOutput method does for you.
Please read the classic reference on this problem: When Runtime.exec() won't. Per this article:
Because some native platforms only provide limited buffer size for standard input and output streams, failure to promptly write the input stream or read the output stream of the subprocess may cause the subprocess to block, and even deadlock.

how to pass the arguments to the shell command through java?

I am new to this kind of integration of java with Unix.
what i am trying to do is
String command="passwd";
Runtime rt=Runtime.getRuntime();
try {
Process pc=rt.exec(command);
try {
pc.waitFor();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
BufferedReader buf = new BufferedReader(new InputStreamReader(pc.getInputStream()));
String line = "";
while ((line=buf.readLine())!=null) {
System.out.println(line);
}
BufferedReader buf1 = new BufferedReader(new InputStreamReader(pc.getErrorStream()));
String line1 = "";
while ((line1=buf1.readLine())!=null) {
System.out.println("Error--"+line1);
}
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
System.out.println("IOException---"+e1.getMessage());
}
now when i am trying to pass the "passwd" command the Unix environment goes to suspended mode.
I want to know how can i pass the old password ,new password and confirm new password to the shell using the java code.
You need to pass it in using the confusing named Process.getOutputStream(). From the doc:
Gets the output stream of the subprocess. Output to the stream is
piped into the standard input stream of the process represented by
this Process object
Note that you need to capture the processes stdout/err simultaneously to avoid blocking. See this answer for more details.
There is a utility called expect. If u installed u can pass argument for any thing. So construct as string execute by
String ConstructedCommand;
Process p=Runtime.getRuntime().exec(ConstructedCommand);
This link will be deserve your need. http://linux.die.net/man/1/expect

jline keep prompt at the bottom

I am using jline and I have a neat ConsoleReader and everything works great. However, if you are typing something into the the prompt and there is output on stdout (from another thread), the output splits the word/command that you are typing.
How can I keep the jline prompt at the bottom of the terminal?
I am using jline 1, but I am open to using jline 2 if it is stable enough.
Finally figured this out... here's what you do. First, define these functions:
private ConsoleReader console = ...;
private CursorBuffer stashed;
private void stashLine() {
this.stashed = this.console.getCursorBuffer().copy();
try {
this.console.getOutput().write("\u001b[1G\u001b[K");
this.console.flush();
} catch (IOException e) {
// ignore
}
}
private void unstashLine() {
try {
this.console.resetPromptLine(this.console.getPrompt(),
this.stashed.toString(), this.stashed.cursor);
} catch (IOException e) {
// ignore
}
}
Then when you want to output new data, first invoke stashLine() to save the current console input, then output whatever new lines of output, then invoke unstashLine() to restore it.

Categories

Resources