How to parse output of a background process with Java? - java

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 ().

Related

Java ProcessExplorer is there a way to allow a remote machine to handle the process run?

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();
}

Java Code stucking when attempt read output messages of Process

I use ProcessBuilder for execute the cmd commands in my application for Start & Stop Derby Network Server. But somethings going wrong and i don't find where the problem. Let me explain it;
Starting Network Server;
//Defining path of db files located
File file= new File(FirstTimeMainFrame.class.getProtectionDomain()
.getCodeSource()
.getLocation()
.getPath().replace(new File(FirstTimeMainFrame.class.getProtectionDomain().getCodeSource().getLocation().getPath()).getName(), "").replace("%20", " "));
String path = file+"\\DB";
//Process Creating
ProcessBuilder builder = new ProcessBuilder();
Process process = null;
String[] command = new String[3];
command[0] = "cmd.exe";
command[1] = "/c"; //This things say to CMD close when commands complete.
command[2] = "cd "+path+" && java -jar derbyrun.jar server start";
builder = new ProcessBuilder(command[0], command[1], command[2]);
builder.redirectErrorStream(true);
process = builder.start();
//Reading CMD outputs
BufferedReader br = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line;
while (true) {
line = br.readLine();
if (line == null) { break; }
System.out.println(line);
}
When i debug the project i see output for two lines and debug stuck at line = br.readLine(); when While loop come and check for third time. Whole program stuck and can't continue.
Outputs;
Fri Dec 25 20:54:36 EET 2015 : Security manager installed using the Basic server security policy.
Fri Dec 25 20:54:36 EET 2015 : Apache Derby Network Server - 10.12.1.1 - (1704137) started and ready to accept connections on port 1527
Important P.S.: If i remove //Reading CMD outputs codes all things working perfectly. Server Starting, DB Created, Tables Created etc.
Also i tried same CMD Command under Windows directly. When i execute the command, two line writed and Command Prompt window stuck at the flashing cursor (not closed or complete i think) but Derby Server is started without problem in programaticly or directly in Windows.
There are actually two processes running in the scenario: CMD process started from java code and Derby server process spawned by CMD.
The output from Derby server process is directed to command line and then can be read in java code. Server process can run infinetly long until it will be terminated, that's why output stream never ends.
Hanging in java code happens because there are no available bytes in the stream at the moment - server process told you that it was successfully initialized and then moved to waiting state.

Why is my process hanging at input.readLine()?

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.

ProcessBulder loads the process but doesn't start it

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);
}
}

Access running java program from shell command

I am looking for a way to access running java program from command line. The best woud be something that does the following:
Starting java app:
bash$java -jar MyBundle.jar App
Accessing app:
bash$test.sh param1 param2
So, test.sh calls App from MyBundle.jar somehow and passes values param1 & param2.
Important: I am looking for very fast approach. App hold database connection and it is very expensive to start App every time I need access do DB.
I need solution that will work in Ubuntu and Debian. If it will work on Mac - great.
Any help is appreciated!
I think you need to take a client-server approach. You app is the server, it runs as a background process and listens for connections on some port. And your client makes requests to the server and gets back the response.
A fast and simple way of implementing this in java would be to wrap your app in the Jetty servlet container. You could set it up to return JSON responses for example, which are easy to process.
It would be quite straightforward to open a TCP/IP socket and use netcat from the shell.
Java code
final ServerSocket serverSocket = new ServerSocket(9050);
while (true) {
final Socket socket = serverSocket.accept();
java.util.logging.Logger.getAnonymousLogger().info("Accepted");
final BufferedReader br = new BufferedReader(new InputStreamReader(
socket.getInputStream()));
final String input = br.readLine();
final BufferedWriter bw = new BufferedWriter(
new OutputStreamWriter(socket.getOutputStream()));
bw.write("You said [" + input + "]");
bw.flush();
socket.close();
}
Shell code
echo 'bla' | nc localhost 9050
You'd need to muck around with threads to keep the sockets open to serve multiple requests.

Categories