I use ProcessBuilder to start a new process (child) form a java application (host). Something like this:
ProcessBuilder processBuilder = createProcess(commandLine);
processBuilder.directory(new File(baseDir));
processBuilder.redirectErrorStream(true);
Process process = null;
try {
process = processBuilder.start();
} catch (Exception e) {
e.printStackTrace();
}
I do see in the system monitor that the child process has been started but it's not functioning unless I stop the host application. More specifically the child proecess is a server and after starting it with a ProcessBuilder it's not responding to the requests if the host application still is running. Moreover, the port that server is using still is available. The server starts working immediately if I stop the host application. Is there anything that I missed or that's how ProcessBuilder suppose to work?
Many thanks in advance.
Under most circumstances, until a process's standard out buffer is emptied, it will not terminate. It might be that your process has filled this buffer and has stopped (for some reason)
Try consuming the processes standard out (via Process#getInputStream) and see if that makes a difference.
It could also be that the process is waiting for input for the user.
Take a look at I'm not getting any output and probably the machine hangs with the code for an example
#MadProgrammer is correct. I was able to fix the issue and I want to answer to my question with a code example. That could be usefull for others too.
You need to consume the standard out of the child process after starting it. Something like this:
process = processBuilder.start();
InputStream is = process.getInputStream();
INputStreamReadr isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
String line;
while (!stopProcess) {
if (br.ready()) {
line = br.readLine();
System.out.println(line);
}
}
Related
working on a problem where ProcessBuilder is being used to start a batch file on a remote machine. However I wan't to have the ability to either wait for the processes completion or not. When waiting everything works perfectly, however when not waiting the process never actually finishes on the remote machine. I believe this is because I am returning immediately after starting the process and this is causing a hang (process which should take 1 second to 1 minutes is running for 15 minutes before finally closing). Is there a way I can get the process to finish and not have to wait for it to complete? Any help would be appreciated.
Path batchFile = pathService.getFilePath(login.getNode(), "run.bat");
// When file does not exists there is no point in executing it
if (!Files.exists(batchFile)) {
return;
}
try {
// Run batch file
Process process = new ProcessBuilder(ImmutableList.of(
"cmd",
"/c",
batchFile.toString()))
.directory(pathService.getRootPath(login.getNode()).toFile())
.redirectErrorStream(true)
.start();
if (!waitForCompletion) {
return;
}
String standardOutput = collectString(process.getInputStream());
String standardError = collectString(process.getErrorStream());
if (!process.waitFor(120, TimeUnit.SECONDS)) {
LOGGER.warn("Process '{}' is hanging with output '{}' and error '{}'", batchFile.toString(), standardOutput, standardError);
}
} catch (IOException | InterruptedException e) {
LOGGER.error(e.getMessage());
}
Note the warnings of the Process javadoc:
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 process may cause the process
to block, or even deadlock.
When you exit your method early with if (!waitForCompletion) then you skip consuming stdout. If your sub-process is writing a lot of output to stdout then it will block if stdout buffer is filled => process freezes.
If that is the case, you can avoid the issue by one of:
Add .inheritIO() when ProcessBuilder to ensure output is passed onto default for current Java process
Send STDOUT/ERR to a file instead, adding .redirectInput(new File("somefile.log"))
Add a background task to consume stdout, something like this but with suitable try/catch in the Runnable for you to deal with IOException / logging etc:
if (!waitForCompletion) {
// Ensure STDOUT is consumed
new Thread(() -> collectString(process.getInputStream())).start();
}
I was making a simple client which connected to another server via Java Sockets and would await messages from that server and modify a video game it is running.
Socket socket = new Socket(server, 6667);
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream( )));
BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream( )));
String line = null;
while ((line = reader.readLine( )) != null) {
System.out.println(line);
}
Currently the while loop occupies the entire main thread and a way to fix that would be to put this process on a separate thread. Best case scenario I would like to create a listener that gets pinged whenever the thread receives a new message and sends it off to interact with the main thread.
I was wondering whether there exists a built in layer for this kind of process in java sockets (like pubsub or onMessage) because it feels like a very popular use case. Please let me know if I've missed something like this in my search, I apologize for my ignorance and thank you in advance.
Edit:
TLDR:
I have a main thread which is being occupied by a game and I would like to read incoming messages from a server, is there any built in way to do it besides employing listeners the manual way?
I'm using Java Process as below :
Runtime rt = Runtime.getRuntime();
try {
Process pr = rt.exec(cmd);
response = IOUtils.toString(pr.getInputStream(), "UTF-8");
response = response.substring(2, response.length()-4);
logger.debug("response :" + response);
} catch (IOException e) {
logger.error(e);
e.printStackTrace();
}
I can see the loggers in my local machine(Mac OSX). But when I run the same program in a Redhat Linux machine, I'm having problem seeing the logs. I don't have any clue whats happening after pr.getInputStream(). Having a logger before that statement is working fine. I want to see the response of the exec(cmd). How can I achieve this. I suspect something is wrong when I'm trying to read the InputStream.
Try reading input stream and error stream in two threads other than the one that launched the process.
I have seen processes gets stuck writing to error or output streams if no one is reading what they are writing.
I have a utility where Jmeter sends a request and the utility sends back response to Jmeter. When load increases, the Utility shuts down with an "EXCEPTION_ACCESS_VIOLATION".
Since it is an error, I am not able to handle it in a catch block. I made a second utility to restart the first utility when the error occurs. Below is the code of the second, restart, utility. In this second utility's code, at the second while, my program sometimes hangs. How do I detect this and restart the process?
public static void main(String[] args)
{
String line = null;
String currPID = null;
try
{
while(true)
{
Process process = Runtime.getRuntime().exec("java -XX:+HeapDumpOnOutOfMemoryError -Xms250M -Xmx500M -XX:ErrorFile=NUL ws ");
BufferedReader input = new BufferedReader(new InputStreamReader(process.getInputStream()));
while ((line = input.readLine()) != null) //Program stucks at this Line
{
if(line.trim().length() != 0)
{
if(line.startsWith("PID"))
{
currPID = line.substring(line.indexOf("#")+1);
}
}
}
System.out.println("Ended");
}
}
catch(Exception e)
{
e.printStackTrace();
}
}
I analysed process through jvisualvm where i found two java process is in running mode when i start second(restart) utility. I can see first utility is restarting regularly because its PID is changing frequently in jvisualvm and same happening in task manager. Everything is going on very well manner.
After sometime i found only one process is in Jvisualvm ie second(restart) utility.
It means first utility JVM is crashed just guessing not sure. Something unusual is happening here. Because if JVM is crashed so It should be restarted.
So i opened task manager and found first utility PID exists there but it is not changing as happening in starting. If i kill the process(first utility) explicitly from task manager.
Seconds utility again restarts first utility same thing happens again, After some time first utility disappeared from jvisualvm, exists in taskmanager and delete process from taskmanager. What needs to do?
Try using .ready() function.
try {
if (stdError.ready())
{
while((line= stdError.readLine()) != null){
logger.error(line);
}
}
}
Do the same for the stdout.
It worked like a charm for me.
Your problem with hanging appears to be at the call to readLine.
readLine is for reading lines. The method will not return until the call is sure the end of line has been reached. It expects either a newline character or the complete cease of communications.
Is your first utility not sending a new line char?
Does your first utility fail to close the stream?
Your while call will hang indefinitely if the answer to both questions is yes.
You might be better off consuming with a custom implementation of the Scanner class.
Try to use getErrorStream() it'll catches the error message, if you use getInputStream() it'll reads only the success message or feedback messages.
for ex: if you execute the following command & read the process message using getInputStream(),
Process process = Runtime.getRuntime().exec("net use u: \\sharedIP\sharedFolder");
BufferedReader input = new BufferedReader(new inputStreamReader(process.getInputStream()));
you can only get feedback messages like "network drive connected successfully" but not the error messages.
if you use getErrorStream() to read the process message it'll read the error messages like "the network drive was not found". when the process executed it'll give a message to either getInputStream() or getErrorStream(). so use both method to read the message from the process, if i'm correct this'll work. I'm just trying to give you an idea but i'm not sure.
In my Java program I would like to start a PostgreSQL server. For this task I am using this code:
ProcessBuilder pb = new ProcessBuilder(pgHome + "/bin/pg_ctl", "start");
Map<String, String> env = pb.environment();
env.put("PGDATA", pgDataDir);
Process p = pb.start();
InputStream is = p.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
String line;
boolean started = false;
while (!started && (line = reader.readLine()) != null) {
started = line.contains("database system is ready to accept connections");
}
if (!started) {
throw new IllegalStateException("Unable to start PostgreSQL server");
}
When I am starting the postgres server on the command line with the same options, I receive:
server starting
LOG: database system was shut down at 2013-02-08 09:07:50 CET
LOG: autovacuum launcher started
LOG: database system is ready to accept connections
The first line (server starting) is printed before the command returns. All following lines are printed by the background process.
My Java code only sees the first line but I want to parse the LOG messages. Is there any way to read an the input of such a background process?
I also encountered a strange and unreproducible behavior: When I debug the code, the output is parsed completely, including the LOG statements. At first I thought it is a timing issue but even waiting after starting the process did not change this.
Most likely pg_ctl is not the actual database program. It's just a script or wrapper program which outputs the first server starting line but then launches the database as a background daemon process and then returns. You cannot attach the output stream of that new process to your program.
The line you want is probably printed to stderr in which case you want to use Process.getErrorStream ().