I'm working on a project that requires me to run an instance of scala using the Process class. To do this I execute the following code:
Process process = RunTime.getRunTime().exec("<path to scala binary> <scala file>");
This call gives me a process representative of the scala program that is executing. I am able to access the process's input stream and receive input from the process with:
BufferedReader stdin = new BufferedReader(new InputStreamReader(process.getInputStream()));
stdin.readLine();
The scala program I am trying to run involves a Scanner which picks up input and prints it out. When I try to register a BufferedWriter to the OutputStreamWriter that is generated from process.getOutputStream(), and then write to that stream with:
stdout.write("Hello");
The scala program does not receive this input, and I was wondering: why does this occur?
Turns out I forgot to flush the stream.
Related
I call a external Python script as Java process and want to send data to this. I create a process and try to send a string. Later the python script should wait for a new input from Java, work with this data and wait again(while true loop).
Python Code (test.py):
input = input("")
print("Data: " + input)
Java Code:
Process p = Runtime.getRuntime().exec("py ./scripts/test.py");
BufferedWriter out = new BufferedWriter(new
OutputStreamWriter(p.getOutputStream()));
BufferedReader stdInput = new BufferedReader(new
InputStreamReader(p.getInputStream()));
System.out.println("Output:");
String s = null;
out.write("testdata");
while ((s = stdInput.readLine()) != null) {
System.out.println(s);
}
The process and output of simple prints works, but not with input and BufferedWriter.
Is it possible to send data to this python input with a Java process?
I read from other solutions:
create a Python listener and send messages to this script
import the external script to Jython and pass data
Are this better solutions to handle my problem?
use Process class in java
what is process class ?
this class is used to start a .exe or any script using java
How it can help you
Create your python script to accept command line variables and send your data from java class to python script.
for Example:
System.out.println("Creating Process");
ProcessBuilder builder = new ProcessBuilder("my.py");
Process pro = builder.start();
// wait 10 seconds
System.out.println("Waiting");
Thread.sleep(10000);
// kill the process
pro.destroy();
System.out.println("Process destroyed");
Later the python script should wait for a new input from Java
If this has to happen while the python process is still a subprocess of the Java process, then you will have to use redirection of I/O using ProcessBuilder.redirect*( Redirect.INHERIT ) or ProcessBuilder.inheritIO(). Like this:
Process p = new ProcessBuilder().command( "python.exe", "./scripts/test.py" )
.inheritIO().start();
If the python process is going to be separate (which is not the case here, I think) then you will have to use some mechanism to communicate between them like client/server or shared file, etc.
I have a minor Problem with a small Project I'm trying to do.
I'm trying to use a Java-Program to call a Python-Script.
Java:
ProcessBuilder pb = new ProcessBuilder("python3", "tmp.py");
process = pb.start();
OutputStream stdin = process.getOutputStream();
InputStream stderr = process.getErrorStream();
InputStream stdout = process.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(stdout));
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(stdin));
writer.write("example" + "\n");
String output = reader.readLine();
Python-Script tmp.py (example):
import sys
sys.stdin.readline()
print("Hello World")
It wont terminate, seemingly because sys.sdin.readline() isnt catching any input and if I remove this line it terminates just fine.
(stderror was empty too)
I tried different things to fix this but nothing seems to work.
I would really appreciate any advice. Thanks in advance.
(Update: I tried to modify the Java-Program to access a .jar File instead of a Python-Script but the same error occurs here to. Using the Python Method subprocess.Popen() to access the Script however works just fine.)
Make sure you start python unbuffered with -u flag:
Force the binary layer of the stdout and stderr streams (which is
available as their buffer attribute) to be unbuffered. The text I/O
layer will still be line-buffered if writing to the console, or
block-buffered if redirected to a non-interactive file.
Edit
I recommend reviewing the buffering all the same. The python unbuffered is a common one, but you possibly still have some buffering. Make sure you flush java writer, writer.flush().
I want to run PianoBar from a Java GUI (PianoBar is a program that runs Pandora from command line). I thought this would be quick and dirty, but I guess I don't know enough about interaction between programs.
I use ProcessBuilder to launch an instance of PianoBar like so:
private Process createPianoBarProcess() throws IOException {
String[] command = {"CMD", "/C", "pianobar"};
ProcessBuilder probuilder = new ProcessBuilder( command );
probuilder.redirectErrorStream(true);
probuilder.directory(new File("~~location where pianobar.exe is~~"));
Process process = probuilder.start();
return process;
}
After I create the process, I create a BufferedReader to read in the PianoBar output:
Process pianoBar = createPianoBarProcess();
InputStream inS = pianoBar.getInputStream();
InputStreamReader isr = new InputStreamReader(inS);
BufferedReader br = new BufferedReader(isr);
But when I read the output from PianoBar via this reader, it spits out the first line of PianoBar ("Welcome to pianobar (2013.05.19-win32)! Press ? for a list of commands."), then it spits out the next line ("[?] Email:"). Then it just hangs.
Obviously, it is waiting for the user to input their email. But no matter what I try, I can't get my Java program to write the email to the PianoBar process when prompted - it just hangs as soon as it reads out the last character.
Is it possible to do what I am trying to do? I thought it would be an easy thing to look for on the internet, but I haven't been able to find anything. All I want is an easy way to write to the external process when prompted. Seems like this should be easy...
You may use the following code snippet to get working:
String s;
//s = email
BufferedWriter bufferedwriter = new BufferedWriter(new OutputStreamWriter(pianoBar.getOutputStream()));
bufferedwriter.write(s);
bufferedwriter.flush();
Done!
Remember to surround the code block with appropriate try/catch
I have a command line Java program which i unfortunately cannot modify for certain integrity reasons, I am providing a GUI for this program and i got it all covered except that i am unable to provide notifications about completion of processes to the user as the program prints data to command line using System.out.println() , I designed the UI using net beans and it resides in a separate file , so how can i do this..?
Are you using Runtime.exec to execute the program? Then you can get the output stream as well like this:
Runtime rt = Runtime.getRuntime();
Process proc = rt.exec("java yourOtherProgram.jar");
InputStream stdin = proc.getInputStream();
If you're calling some method directly, you could redirect System.out like this:
PrintStream out = ...;
PrintStream err = ...;
System.setOut(out);
System.setErr(err);
ThatOtherProgram foo = new ThatOtherProgram();
foo.main(new String[0]);
EDIT
You could then use the Scanner to scan the input and do something with it.
Everything works fine on the command line, but when I translate what I want into Java, the receiving process never gets anything on stdin.
Here's what I have:
private void deployWarFile(File warFile, String instanceId) throws IOException, InterruptedException {
Runtime runtime = Runtime.getRuntime();
// FIXME(nyap): Use Jsch.
Process deployWarFile = runtime.exec(new String[]{
"ssh",
"gateway",
"/path/to/count-the-bytes"});
OutputStream deployWarFileStdin = deployWarFile.getOutputStream();
InputStream deployWarFileStdout = new BufferedInputStream(deployWarFile.getInputStream());
InputStream warFileInputStream = new FileInputStream(warFile);
IOUtils.copy(warFileInputStream, deployWarFileStdin);
IOUtils.copy(deployWarFileStdout, System.out);
warFileInputStream.close();
deployWarFileStdout.close();
deployWarFileStdin.close();
int status = deployWarFile.waitFor();
System.out.println("************ Deployed with status " + status + " file handles. ************");
}
The script 'count-the-bytes' is simply:
#!/bin/bash
echo "************ counting stdin bytes ************"
wc -c
echo "************ counted stdin bytes ************"
The output indicates that the function hangs at the 'wc -c' line -- it never gets to the 'counted stdin bytes' line.
What's going on? Would using Jsch help?
You might try closing the output stream before you expect wc -c to return.
IOUtils.copy(warFileInputStream, deployWarFileStdin);
deployWarFileStdin.close();
IOUtils.copy(deployWarFileStdout, System.out);
warFileInputStream.close();
deployWarFileStdout.close();
Would using Jsch help?
Using JSch would only help if you would be using the setInputStream() and setOutputStream() methods of the channel instead of the IOUtils.copy method, since they manage the copying on a separate thread.
ChannelExec deployWarFile = (ChannelExec)session.openChannel("exec");
deployWarFile.setCommand("/path/to/count-the-bytes");
deployWarFile.setOutputStream(System.out);
deployWarFile.setInputStream(new BufferedInputStream(new FileInputStream(warFile)));
deployWarFile.connect();
(Here you somehow have to wait until the other side closes the channel.)
If you simply replaced the Runtime.exec with opening an ChannelExec (and starting it after getting the streams), the problem would be completely the same, and could be solved by the same solution mentioned by antlersoft, i.e. closing the input before reading the output:
ChannelExec deployWarFile = (ChannelExec)session.openChannel("exec");
deployWarFile.setCommand("/path/to/count-the-bytes");
OutputStream deployWarFileStdin = deployWarFile.getOutputStream();
InputStream deployWarFileStdout = new BufferedInputStream(deployWarFile.getInputStream());
InputStream warFileInputStream = new FileInputStream(warFile);
deployWarFile.connect();
IOUtils.copy(warFileInputStream, deployWarFileStdin);
deployWarFileStdin.close();
warFileInputStream.close();
IOUtils.copy(deployWarFileStdout, System.out);
deployWarFileStdout.close();
(Of course, if you have longer output, you will want to do input and output in parallel, or simply use the first method.)
You probably get an error, but the process hangs because you are not reading the error stream.
Taken from the Process JavaDoc
All its standard io (i.e. stdin, stdout, stderr) operations will be redirected to the parent process through three streams (Process.getOutputStream(), Process.getInputStream(), Process.getErrorStream()). The parent process uses these streams to feed input to and get output from the subprocess. 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.
So you need to read all of them. Using the ProcessBuilder is probably easier