Infinite interprocess communication between java program and a python script - java

I want to achieve interprocess communication between my java app and the python script that it calls through its code.
The idea is that the python script runs in an infinite while loop, accepts input, processes the input and prints the result in the console.
Meanwhile, the java app sends the input, waits for the python script to process it, and reads it from the console (using InputStream from the python process, omitted for brevity) when the python script is done processing. How would one go about achieving this type of communication?
Java program
import java.io.*;
public class JavaProgram {
public static void main(String[] args) throws IOException{
Process p = Runtime.getRuntime().exec("python pythonScript.py");
OutputStream os = p.getOutputStream();
os.write("Hello".getBytes());
os.close();
os = p.getOutputStream();
os.write("World".getBytes());
os.close();
os = p.getOutputStream();
os.write("quit".getBytes());
os.close();
}
}
Python script
def transformString(string):
return "Java said: " + string
file = open("writingFile.txt", "w+")
while True:
user_input = input('\n> ')
message = transformString(user_input)
if message == "quit":
break
file.write(message + "\n")
print(message + "\n")
file.write("closing file...\n")
file.close()
The problem with the above code is that the java app can only send the input once (only "Hello" is sent) and then closes the output stream (tried without closing it). I know this because the python script never finished.
Here is the contents of the file writingFile.txt:
Java said: Hello
*Note: It's really important that the python script is started once, because the original script's init methods are time-consuming.
*Second note: The python's processing method also takes a bit of time, so how I don't know how to tell the java program when it's should start reading the output.

Related

Passing data from Java process to a python script

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.

Executing a WMIC command line argument in Java

I'm trying to use the WMIC command 'DESKTOP' in an application through a method which is implemented from an interface. Before using WMIC commands in cmd, it's necessary to first input 'WMIC' and press enter, as I'm sure you all know. I can do this just fine, however after the command line enters WMIC mode, I need to subsequently enter the DESKTOP command, or any other WMIC command (see list : http://ss64.com/nt/wmic.html). Here's the overridden method code I'm currently using to enter WMIC mode :
#Override
public void Desktop(){
try {
Runtime rt = Runtime.getRuntime();
String cmd = "cmd /c WMIC";
Process pr = rt.exec(cmd);
}
Perhaps I'm going about this the wrong way? Any help would be much appreciated.
You need to get the output stream of the process you started and write to it.
OutputStream os = pr.getOutputStream();
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(os));
bw.write("DESKTOP");
bw.newLine();
bw.flush();
If you need to see the output/errors from the process you started you need to use getInputStream() and getErrorStream() methods to get the output/error streams and read from it.
Check the javadoc of Process for more details.
http://docs.oracle.com/javase/7/docs/api/java/lang/Process.html
Pay special attention to this part -
By default, the created subprocess does not have its own terminal or
console. All its standard I/O (i.e. stdin, stdout, stderr) operations
will be redirected to the parent process, where they can be accessed
via the streams obtained using the methods getOutputStream(),
getInputStream(), and 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, or even deadlock.
It's a fairly silly answer.
WMIC is normally used in non interactive mode.
wmic desktop get
However you are a programmer. Therefore you are supposed to program.
Here's vbs script that does it. The help is filled with sample scripts in C, VB, VBS, and JScript.
Set objWMIService = GetObject("winmgmts:{impersonationLevel=impersonate}!\\.\root\cimv2")
Set colItems = objWMIService.ExecQuery("Select * From Win32_Desktop")
For Each objItem in colItems
msgbox objItem.Name & " " & objItem.Wallpaper
Next

Execute command from Java

I'm trying to execute a script from Java program:
public class TestCommandLine
{
public static void main (String[] args)
{
String PATH = "/path/programs/";
String command = PATH + "name_programs param1 param2";
executeCommand (command);
}
private static String executeCommand (String command)
{
StringBuffer output = new StringBuffer();
String line = "";
Process p;
try {
p = Runtime.getRuntime ().exec (command);
p.waitFor ();
BufferedReader reader = new BufferedReader (new InputStreamReader (p.getInputStream ()));
while ((line = reader.readLine ()) != null) {
output.append (line + "\n");
}
}
catch (Exception e) {
e.printStackTrace ();
}
return output.toString ();
}
}
there is not error, but the program does not run. I also try others solutions from stackoverflow but all of them didn't work
If you'd given your actual command to start with, this would have been much quicker.
You cannot use Process.exec to run shell-interpreted commands. Instead it executes programs directly. Thus input/output redirection (|, >, etc.) is not possible.
If you actually read the stderr (getErrorStream()) output it would probably be along the lines of "invalid argument: >".
You will either have to:
Redirect the output in Java. Read from the process's stdout (getInputStream()) and write to a FileOutputStream of some kind.
Execute a shell instead of your command directly. For example /bin/sh -c "command arg > file". The quoted section must be passed to sh as a single argument. In this case you wouldn't be able to see anything in stdout, and would have to open and read the file you just wrote to. The first option is probably more sensible.
And as pointed out elsewhere, unless your expecting a very small amount of output, you shouldn't wait for the command to exit before consuming the streams.
The only time I've done it I've used something like this:
Process p = Runtime.getRuntime().exec("foo.exe");
Have you tried that? If so, what was the error you got back?
Your command is producing output that you want to read, but you refuse to read any of it until the command has finished producing it all and has exited (not calling getInputStream() until after waitFor()).
If your command doesn't produce much output, this is OK, Java can buffer it. But if your command produces a lot of output, Java can't buffer it all, and the command gets blocked.
The operating system won't let the command write any more output because Java's buffer is full and you haven't instructed Java to empty it. So the program is blocked, and Java's waitFor() will never come back.
To solve your problem, you should call getInputStream() immediately after getting the Process object back from exec(), and you should create a new Thread that is responsible for reading the command output into your StringBuffer.
You should then waitFor() the process to finish, to see if it exited successfully, and then you can wait for the thread to get to the end of the inputstream and finish - at that point, it is safe to read through the StringBuffer with the full output from your command.

Open an independent terminal window and run command with Java

Basically, I'm trying to open a terminal window and command it to start a php script.
Also being able to input and get the output immediately when a the script output a new line to the terminal. If possible, i plan to make it hidden.
More like mirroring the output and input from the terminal into the java app itself
I did
Runtime.getRuntime().exec("/usr/bin/open -a Terminal ~/Desktop/test.php"); //mac
I have no idea how to input and get output immediately once the php script sends an output to the terminal.
Please do help me here
create process and read input stream of process.
(javadoc) getInputStream()
Gets the input stream of the subprocess. The stream obtains data piped from the standard output stream of the process represented by this Process object.
//-->check command line<--
Process process = Runtime.getRuntime().exec("/usr/bin/php /home/amit/hello.php");
BufferedInputStream iStream = new BufferedInputStream(process.getInputStream());
BufferedOutputStream oStream = new BufferedOutputStream(process.getOutputStream());
byte[] buffer = new byte[1024];
while (true){
int length = iStream.read(buffer);
if(length == -1)
break;
System.out.println(new String(buffer, 0, length));
}
Note: have written with respect to Linux.
Instead of executing the php script in the terminal, which in turns uses php to execute the script, just execute the php script in php and capture the input/output.
$ which php
/usr/bin/php

Java stuck in infinite loop executing a wmic command on Windows Server 2003

I'm trying to get a list of running processes and their file paths on a Windows Server 2003 machine. I'm using the following code to try and do that:
protected Map<String,String> getProcesses() {
Map<String,String> processes = new HashMap<String,String>();
try {
String line;
Process p = null;
// Windows
if (OS.indexOf("win") >= 0) {
p = Runtime.getRuntime().exec("wmic process get description,executablepath");
BufferedReader input = new BufferedReader(new InputStreamReader(p.getInputStream()));
LOG.info("Entering while loop");
while ((line = input.readLine()) != null) {
LOG.info("blah");
String[] array = line.split("\\s+");
if (array.length > 1) {
processes.put(array[0], array[1]);
}
}
LOG.info("Exited while loop");
input.close();
}
} catch (Exception e) {
e.printStackTrace();
}
return processes;
}
The program gets stuck in an infinite loop at the while condition. "blah" and "Exited while loop" never output to the log. I've ran the command in command prompt on both my win7 local machine and the server which outputs the information just fine. I've also ran the above code on my local machine which also works fine. It looks like it's some issue between Java and Windows Server 2003 that I haven't been able to find in the past 3 hours of googling. Any help would be much appreciated.
You will need to get and close your OutputStream before getting and using your InputStream. That will confirm to the process that you've started that you have finished sending input (in this case, no input) to the process.
p.getOutputStream().close();
Remember that on the Process object, getInputStream() input comes from the output stream of the process, and getOutputStream() output goes to the input stream of the process.
Remember the BufferedReader.readLine() operation will block if there the end of input has not been reached, see here.
I think what you are experiencing is explained in the API for Process:
The methods that create processes may not work well for special processes on certain native platforms, such as native windowing processes, daemon processes, Win16/DOS processes on Microsoft Windows, or shell scripts. The created subprocess does not have its own terminal or console. All its standard io (i.e. stdin, stdout, stderr) operations will be redirected to the parent process through three streams (getOutputStream(), getInputStream(), 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.

Categories