Run python script from java as subprocess - java

I'm trying to execute python code (live from the console, not just opening a single file).
ProcessBuilder builder = new ProcessBuilder("python");
Process process = builder.start();
new Thread(() -> {
try {
process.waitFor();
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
new Thread(() -> {
String line;
final BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
// Ignore line, or do something with it
while (true) try {
if ((line = reader.readLine()) == null) break;
else System.out.println(line);
} catch (IOException e) {
e.printStackTrace();
}
}).start();
final PrintWriter writer = new PrintWriter(new OutputStreamWriter(process.getOutputStream()));
writer.println("1");
writer.println("2 * 2");
I tried this code, but after trying to push the following expressions 1 and 2*2, I don't get a response (evaluation of my expressions).
Does anyone know what the issue is?

Your python code doesn't appear to print anything, and handling multiple threads to read and write to another process is a tricky topic; luckily, the functionality is built-in. You could do
ProcessBuilder builder = new ProcessBuilder("/usr/bin/env", "python",
"-c", "print(2*2); exit()");
builder.inheritIO();
try {
Process process = builder.start();
process.waitFor();
} catch (Exception e) {
e.printStackTrace();
}
Which outputs
4
and terminates. For less trivial Python and Java integration, I would strongly suggest you look here. As for your existing code, you never exit() python and you never flush() your PrintWriter. And you write on the main thread. And you need to pass -i to python, or it won't assume stdin is a console. Changing your code to
ProcessBuilder builder = new ProcessBuilder("/usr/bin/env", "python", "-i");
Process process = builder.start();
new Thread(() -> {
String line;
final BufferedReader reader = new BufferedReader(
new InputStreamReader(process.getInputStream()));
// Ignore line, or do something with it
while (true)
try {
if ((line = reader.readLine()) == null)
break;
else
System.out.println(line);
} catch (IOException e) {
e.printStackTrace();
}
}).start();
new Thread(() -> {
final PrintWriter writer = new PrintWriter(
new OutputStreamWriter(process.getOutputStream()));
writer.println("1");
writer.println("2 * 2");
writer.println("exit()");
writer.flush();
}).start();
Seems to work properly.

Related

Running python with command line in java

The program is stuck at p2.waitFor() (I tested with printing strings before and after)
public void score() {
this.toXML();
try {
Process p = Runtime
.getRuntime()
.exec("python sumocfg_maker.py Carrefour.net.xml Detectors.det.xml edgedata.csv -ef");
p.waitFor();
Process p2 = Runtime.getRuntime().exec("python simulation.py");
new Thread(new Runnable() {
public void run() {
BufferedReader input = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line = null;
try {
while ((line = input.readLine()) != null)
System.out.println(line);
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
p2.waitFor();
} catch (Exception e) {
e.printStackTrace();
}
}
and simulation.py is
import os
os.system('cmd /c "sumo -c Simulation.sumocfg --duration-log.statistics --log duration.txt)
The simulation.py runs fine on its own. When I put the command in simulation.py in java, I get the same problem.
The System.out.println(line); prints out "Success" and then nothing
I left out code from simulation.py that saves a file that the java reads right after the p2.wait(), and without the p2.wait() the file never changes.
You have
BufferedReader input = new BufferedReader(new InputStreamReader(p.getInputStream()));
But you need
BufferedReader input = new BufferedReader(new InputStreamReader(p2.getInputStream()));
since p is already finished, the bufferedreader will wait but never receive anything.

How can I have a callback on stdout/stderr readiness instead of busy polling?

All the discussions I found of reading from Process stdout do it in a blocking way, like here:
Process process=Runtime.getRuntime().exec(cmd);
BufferedReader in=new BufferedReader(
new InputStreamReader(process.getInputStream()));
for(String line;(line=in.readLine())!=null;)
{
useText(line);
}
If I try to avoid blocking, my option is to poll the stream using in.ready().
But what I really need is to leave the process running and do something useful, and only try to read anything when the data is actually available. So I'm looking for something like Qt's QProcess::readyReadStandardOutput signal. Note that BufferedReader's nethod ready is not what I'm looking for: it's simply a method I would have to repeatedly check, rather than any sort of signal.
Is there anything like that in Java?
If you have an event loop, you can start a background thread to add events to that event loop.
e.g.
ProcessBuilder pb = new ProcessBuilder(cmd);
pb.redirectErrorStream(true); // capture errors as well.
Process process = pb.start();
BufferedReader in = new BufferedReader(
new InputStreamReader(process.getInputStream()));
Thread t = new Thread(() -> {
try {
for (String line; (line = in.readLine()) != null; ) {
Activity.runOnUiThread(() -> useText(line));
}
} catch (Exception e) {
logger.log(e);
} finally {
Activity.runOnUiThread(() -> noMoreText());
}
});
t.setDaemon(true);
t.start();
You can have a thread which only does queue.add(in.readLine()) then your main thread can poll this queue. That way all multi-threading issues are simple and contained.
static final String EOF = new String(); // use for == comparison later
ProcessBuilder pb = new ProcessBuilder(cmd);
pb.redirectErrorStream(true); // capture errors as well.
Process process = pb.start();
BufferedReader in = new BufferedReader(
new InputStreamReader(process.getInputStream()));
int capacity = 1024;
BlockingQueue<String> lines = new ArrayBlockingQueue<>(capacity);
Thread t = new Thread(() -> {
try {
for (String line; (line = in.readLine()) != null; ) {
lines.add(line);
while (lines.remainingCapacity() < 2) // don't run out of memory if too much.
Thread.sleep(50);
}
} catch (Exception e) {
logger.log(e);
} finally {
lines.offer(EOF);
}
});
t.setDaemon(true);
t.start();
// later in a loop
String line = lines.poll();
if (line == null) // no data yet.
else if (line == EOF) // we have the EOF marker.

Trying to run an exe file created from a cpp file in java

I'm trying to run an executable file created from a cpp program in java. If I double-click the exe file, it works just fine, but if I run the file using ProcessBuilder, it gets stuck for some reason, it prints most of the expected output and doesn't continue, also making the entire Java program not responding.
here's my code:
String filePath = FirstScreenController.getFile().getPath();
ProcessBuilder launcher = new ProcessBuilder("ClusteringProgram\\Release\\main.exe",filePath);
launcher.redirectErrorStream(true);
try {
/*File file = FirstScreenController.getFile();
Path newPath = Paths.get(System.getProperty("user.dir")+"\\ClusteringProgram").resolve("K12.fasta");//Moving the file to the
Files.copy(Paths.get(file.getPath()), newPath, StandardCopyOption.REPLACE_EXISTING);*/
System.out.println("Execution started");
p = launcher.start();
InputStream stderr = p.getInputStream();
InputStreamReader isr = new InputStreamReader(stderr);
BufferedReader br = new BufferedReader(isr);
String line = null;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
p.waitFor();//Waiting for the process to finish running
System.out.println("Execution completed");
} catch (IOException | InterruptedException e) {e.printStackTrace();}
Close your stream. That's what's causing you to hang. I write code like this quite a bit.
while ((line = br.readLine()) != null) {
System.out.println(line);
}
br.close(); // You need this or p can hang
p.waitFor();
In addition, you called launcher.redirectStandardError(true); so you actually need all this to gather both stdout and stderr together: The whole rest of this answer is wrong. I don't know what is causing the deadlock. I'm leaving the large code fragment here in case it's some strange library bug and it turns out that the dual-thread reading technique is required to work around it.
final object lock = new object();
InputStream stdout = p.getInputStream();
InputStreamReader isr = new InputStreamReader(stdout);
BufferedReader br = new BufferedReader(isr);
final InputStream stderr = p.getErrorStream();
one = new Thread() {
public void run() {
InputStreamReader isr2 = new InputStreamReader(stderr);
BufferedReader br2 = new BufferedReader(isr2);
while ((line2 = br2.readLine()) != null) {
synchronized(lock) {
System.out.println(line2);
}
}
br2.close(); // you need this or p can hang
}
};
one.start();
while ((line = br.readLine()) != null) {
synchronized(lock) {
System.out.println(line);
}
}
br.close(); // You need this or p can hang
for (;;) {
try {
one.join();
break;
} catch (InterruptedException v) {
/* if there's something that might want the main thread's attention handle it here */
}
}
p.waitFor();

Best way to do error handling while executing a script using getRuntime().exec()

I have a code where I am using getRuntime().exec() to execute a script as:
Process pr = Runtime.getRuntime().exec(cmd);
BufferedReader bfr = new BufferedReader(new InputStreamReader(pr.getInputStream()));
String line = "";
while((line = bfr.readLine()) != null) {
// WHAT TO DO HERE?
}
Most of the time the script will execute commands which perform some action without returning any line as in copy a file from one location to another or something on this line. I want to know how effectively can I do error handling in this case? I mean if the script returns something than in the above code those are shown as the output inside the while loop but in this case nothing is returned to be displayed. So how can I get error handling here?
I think this is jave code but not Python. You put the wrong tag.
Here are some hints for you. Hope it helps.
...
// define your cmd here
Process p = Runtime.getRuntime().exec(cmd);
final InputStream is = p.getInputStream();
Thread t = new Thread(new Runnable() {
public void run() {
InputStreamReader isr = new InputStreamReader(is);
int ch;
try {
while ((ch = isr.read()) != -1) {
System.out.print((char) ch);
}
} catch (IOException e) {
e.printStackTrace();
}
}
});
t.start();
p.waitFor();
t.join();
...

Redirect file I/O of sub process java

I am opening a new file and trying to write to it inside a sub process in java. I used the process builder to start the sub process. The file is being created but whatever content I want to write to the file is not getting written. Is there a way to solve this? I can redirect stdin, stdout and stderr of sub process but how to redirect the file I/O. I want to do it on java version 1.7
process is started:
ArrayList<String> params = new ArrayList<String>();
for (int i = 0; i < cmdarray.length; i++) {
params.add(cmdarray[i]);
}
try {
//java.lang.Process process = Runtime.getRuntime().exec(cmdarray);
ProcessBuilder builder = new ProcessBuilder(params);
final java.lang.Process process = builder.start();
BufferedReader br = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line;
while((line = br.readLine()) != null) {
System.out.println(line);
}
}
catch (IOException e) {
e.printStackTrace();
}
file is created inside the sub process:
BufferedWriter br;
try {
br = new BufferedWriter(new FileWriter(new File("text.txt")));
br.write("This should be present in the file.");
}
catch (IOException e) {
e.printStackTrace();
}
You forget to close the BufferedWriter br, this is why the String is not getting written in the file.
Try this, may help :
BufferedWriter br;
try {
br = new BufferedWriter(new FileWriter(new File("text.txt")));
br.write("This should be present in the file.");
br.close();
} catch (IOException e) {
e.printStackTrace();
}

Categories

Resources