Output is incomplete when running jdb as a process from java application - java

I'm trying to debug a java class from java program. I created a simple class to test. This is my class hello.java and it lies in the folder C:\Users\madhawax\Desktop\beaufify\debugging
My problem is that I can't retrieve the part
VM Started: Set deferred breakpoint Hello.main
...
when I run jdb from java code, but when I manually run jdb from command line I can see it.
Why do I get only part of the real output? How can I fix this?
This is my Hello.java class:
public class Hello {
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
System.out.println("loop number "+i);
}
}
}
I used 3 commands to run jdb
jdb
stop in Hello.main
run Hello
Console output when I debug manually using cmd .
C:\Users\madhawax\Desktop\beaufify\debugging>jdb
Initializing jdb ...
> stop in Hello.main
Deferring breakpoint Hello.main.
It will be set after the class is loaded.
> run Hello
run Hello
Set uncaught java.lang.Throwable
Set deferred uncaught java.lang.Throwable
>
VM Started: Set deferred breakpoint Hello.main
Breakpoint hit: "thread=main", Hello.main(), line=3 bci=0
3 for (int i = 0; i < 10; i++) {
main[1]
Output when I run jdb using java code .
run:
Initializing jdb ...
> Deferring breakpoint Hello.main.
It will be set after the class is loaded.
> run Hello
Set uncaught java.lang.Throwable
Set deferred uncaught java.lang.Throwable
> Input stream closed.
BUILD STOPPED (total time: 4 seconds)
I used this code to run jdb.
try {
ProcessBuilder builder = new ProcessBuilder("C:\\Program Files\\Java\\jdk1.8.0_31\\bin\\jdb.exe");
builder.directory(new File("C:\\Users\\madhawax\\Desktop\\beaufify\\debugging\\"));
Process process = builder.start();
OutputStream stdin = process.getOutputStream();
InputStream stdout = process.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(stdout));
try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(stdin))) {
writer.write("stop in Hello.main\n");
writer.flush();
writer.write("run Hello");
writer.flush();
}
String inputLine;
Scanner scanner = new Scanner(stdout);
while (scanner.hasNextLine()) {
System.out.println(scanner.nextLine());
}
} catch (IOException ex) {
ex.printStackTrace();
}

I suggest you to run your code without try-with-resources:
Scanner scanner = new Scanner(stdout);
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(stdin));
writer.write("stop in Hello.main\n");
writer.flush();
writer.write("run Hello\n");
writer.flush();
while (scanner.hasNextLine()) {
System.out.println(scanner.nextLine());
}
In your code try-with-resources will close BufferedWriter after execution:
try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(stdin))) {
writer.write("stop in Hello.main\n");
writer.flush();
writer.write("run Hello");
writer.flush();
} //writer is closed here
Thus, it will close underlying process output stream and this, apparently, leads to closure of jdb process.
You might want to change try-with-resource to try-catch-finally wrapping the whole method.
UPDATE: Also, it's a good idea to read output of one command before running next command. With your approach:
writer.write("stop in Hello.main\n");
writer.flush();
writer.write("run Hello\n");
writer.flush();
writer.write("list\n");
...
There's no pause between command calls. jdb might not be able to handle list command at the time (because it's launching VM). As an experiment you can introduce a time gap:
writer.flush();
Thread.sleep(1000);
writer.write("list\n");
The superior approach is to read output in between.
writer.flush();
readOutput(stdout);
writer.write("list\n");
You can use scanner to read the output. But, as #vandale pointed out in question comments, Scanner blocks on token and line breaks. You might want to use non-blocking reads to read available output. Something like this might work:
private void readOutput(InputStream outputStream) throws IOException{
byte[] buffer = new byte[100000];
int bytesRead;
while (outputStream.available() > 0) {
bytesRead = outputStream.read(buffer);
if (bytesRead > 0) {
System.out.print(new String(buffer, 0, bytesRead));
}
}
}
This code will also show output that doesn't end with a line break (input prompts, main[1], etc.)

Related

BufferedReader not seeing end-of-file on StdIn when run under Eclipse

I'm reading standard input:
public static void main(String[] args) {
try {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
for (;;) {
var line = br.readLine();
if (line == null) break;
System.out.println(line);
}
} catch (Exception e) {
e.printStackTrace();
}
System.err.println("Done.");
}
If I use this in a terminal:
java TheClass </path/to/file
... it works: the program runs to completion.
But under Eclipse, if I set the same file as standard input (Run Configurations>Common>Standard Input and output), the program reads the file but it hangs after the last line until I enter a Ctrl-D in the console window (and then terminates normally).
Also under Eclipse, if I use the file directly (FileInputStream) the file is read to the end and the program terminates immediately.
Am I missing something or is this an Eclipse problem?
Using Eclipse 2020-06 on Linux with openjdk 11.
Sounds like Eclipse bug: 513713 - In Run configuration when reading standard input from file, EOF is not issued at the end

Send multiples commands with ProcessBuilder?

I want to spawn a child process in Java and send different commands inside the app. My child process has authentication and each user can have a variety of internal commands.
For example:
> login myuser passowrd
OK
> list certs
cert1 abc
cert2 efg
> logout
> exit
Well, to simulate that I will make my example with "node" as IO CLI.
public class JAVAMain {
public static void main(String[] args) throws Exception {
action();
}
public static String action() throws Exception {
ProcessBuilder pb = new ProcessBuilder("node");
pb.redirectErrorStream(true);
Process process = pb.start();
// streams
InputStream stdout = process.getInputStream();
OutputStream stdin = process.getOutputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(stdout));
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(stdin));
String buff = "";
String res = "";
System.out.println("1");
writer.write("console.log('OK');\n"); // simulate "login answer"
writer.flush();
System.out.println("2");
res = "";
while ((buff = reader.readLine()) != null) {
res += buff;
}
if (!res.equals("OK")) {
reader.close();
writer.close();
throw new Exception("Invalid auth");
}
System.out.println("3");
writer.write("console.log('any text...');\n");
writer.flush();
System.out.println("4");
res = "";
while ((buff = reader.readLine()) != null) {
res += buff;
}
reader.close();
writer.close();
return res;
}
}
I expect to print 1,2,3,4 and get the res any text... for this example. But the program never stops and stay in 1,2. If I close the writer after the flush in 1 I get this output:
1
2
3
Exception in thread "main" java.io.IOException: Stream closed
at java.io.BufferedWriter.ensureOpen(BufferedWriter.java:116)
at java.io.BufferedWriter.write(BufferedWriter.java:221)
at java.io.Writer.write(Writer.java:157)
at com.keynua.kades.JAVAMain2.action(JAVAMain2.java:48)
at com.keynua.kades.JAVAMain2.main(JAVAMain2.java:14)
That's why I close the writer and the reader works but I can't write again. So, how I can make to send multiples commands to the child app and read the output to follow the flow with other commands?
One problem is that your while loops
while ((buff = reader.readLine()) != null) { ... }
only terminate when the reader has reached the end of the input stream.
The end of the input stream is only reached when the subprocess terminates.
The second problem is that you seem to be using NodeJS as sample command executor.
If NodeJS is started from a console, you can enter JavaScript statements and they are executed one by one.
You are however starting NodeJS not from a console, but from some other application. In this case, NodeJS wants to read a complete script from stdin and executes the complete script at once
You could start NodeJS with the -i parameter (force interactive mode), at the expense of some additional output.
To achieve this, you would create the ProcessBuilder with
ProcessBuilder pb = new ProcessBuilder("node", "-i");
Communicating with a subprocess in this way only works when you know how many lines to read from the reader before sending the next command.
Knowing how many lines to read can mean:
knowing how many lines of output a command produces (login: 1 line of output, logout: no output)
knowing that a command produces a distinct last line (for example an empty line or a line with only "END" in it)
that a command produces a line count as the first result
executing another command first that returns the result line count of the subsequent command
The list certs command could either:
produce
cert1 abc
cert2 def
END
produce (where the last line would be empty instead of containing a dot)
cert1 abc
cert2 def
.
produce
2 certs
cert1 abc
cert2 def
or you could execute a count certs command before executing list certs

java ProcessBuilder: run program with multiple input

I use a ProcessBuilder to run system command from java. The system command may ask input data from user. Program failed when the system command asks input data from user for multiple times. Example of running such a command from command-line directly:
>test-input
Continue? Y/N
y
Entered y
Again: Continue? Y/N
y
Entered y
If I use my ProcessBuilder based program to run "test-input", it either hangs or failed to take input for a second time. Here is the code of reading/writing logic. Read from input stream (Exception handling and stream close logic is omitted)
ProcessBuilder pb = new ProcessBuilder(cmdList);
pb.redirectErrorStream(true);
pb.directory(new File("some-test-dir"));
process = pb.start();
InputStream is = process.getInputStream();
int value = -1;
while ( (value = is.read()) != -1) {
reader.append((char)value);
}
int result = process.waitFor();
Write to output stream:
public void write(String s) {
OutputStream os = null;
try {
os = process.getOutputStream();
os.write(s.getBytes(Charset.forName("UTF-8")));
}
catch (IOException e) {
//...
}
finally {
// Problematic
os.close();
}
}
The problem occurred at the line os.close(). If I put it there, the output stream is closed after the first input data is processed, thus it cannot be re-opened and program cannot take the second input data. If I do not close the output stream, then program hangs there as is.read() gets blocked forever. How to solve this issue? thanks
Problem is fixed by writing a new line character for each input, as described in: Writing to InputStream of a Java Process
os.write(s.getBytes(Charset.forName("UTF-8")));
os.write('\n');
os.flush();

Java program using ProcessBuilder with input and output streams doesn't work

I have searched through similar questions here on Stack Overflow, however, I can't seem to make this work.
I have a java program that must use ProcessBuilder to load a C executable file. The file just accepts a string through the CLI and converts it to upper case. The java program creates a system process with ProcessBuilder to manage this executable, and must send the string and receive the converted one to print it in the CLI.
This is the code for uppercases.c:
#include <stdio.h>
int main(int argc, char *argv[]) {
char text[1024];
scanf("%s", &text[0]);
int i;
for(i = 0; i < strlen(text); i++) {
text[i] = toupper(text[i]);
}
printf("%s\n", text);
return 0;
}
I compiled it using
$ gcc uppercases.c -o uppercases
And ran it with
$ ./uppercases
Everything works fine. Now this is the code for Uppercase.java. I have to create an OutputStream to send the string to the C executable (uppercases), and then I create an InputStream to save its output and print it to the CLI:
public class Uppercase {
public static void main(String[] command) {
String textIn, textOut;
Scanner reader = new Scanner(System.in);
// This is what we want to send
System.out.println("Write something: ");
textIn = reader.nextLine();
try {
// Here I create a process to handle "uppercases"
Process process = new ProcessBuilder(command).start();
OutputStream os = process.getOutputStream();
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(os));
// According to me, this will send the string to "uppercases"
bw.write(textIn);
bw.flush();
// This is to read the output of "uppercases"
InputStream is = process.getInputStream();
BufferedReader br = new BufferedReader (new InputStreamReader(is));
while ((textOut = br.readLine()) != null) {
System.out.println(textOut);
}
os.close();
is.close();
} catch (IOException e) {
System.out.println("I/O error:" + e.getMessage());
} catch (Exception e) {
System.out.println("Error:" + e.getMessage());
}
}
}
To compile, I type:
$ javac Uppercase.java
And to execute:
$ java Uppercase ./uppercases
The problem is that when I type a string and hit enter, the cursor stays forever there, I hit enter again and nothing happens, and finally I have to press CTRL+C to exit. Any help will be appreciated.
All works fine with your java program with one exception : you use a BufferedWriter that you correctly flush, but as the line that you write does not contain a newline, the C program still waits for more input.
If you write :
// According to me, this will send the string to "uppercases"
bw.write(textIn);
bw.write("\n");
bw.flush();
It is enough and the program terminates normally.
But if you really want the things to be bullet proof, you should close bw to clearly indicate to the subprocess that is should not wait for more input :
// According to me, this will send the string to "uppercases"
bw.write(textIn);
bw.close();
Then the program terminates normally (and correctly) even without the end new line. Of course in that case the last os.close() is no longer necessary but is still harmless.

runtime.exec() taking infinite time to execute code

I want to execute a command which takes 2 arguments.
1.input file name
2.output file name.
The command is sixV1.1 outputFile.txt
The code is:
String cmd= "sixV1.1 <inputFile.txt >outputFile.txt";
Process p=Runtime.getRuntime().exec(cmd);
int retValue=p.waitFor();
when the i run above code,it is taking infinite time.
Is it possible to give <, > charecters in cmd .Please suggest me....
The right way to do input/output redirection when you start a process in Java is to write/read from the process's streams:
Process p = Runtime.getRuntime().exec("sixV1.1");
InputStream is = p.getInputStream();
// read from is and write to outputFile.txt
OutputStream os = p.getOutputStream();
// read from inputFile.txt and write to os
There's a fantastic blog post by Michael C. Daconta about successful command line calls using Runtime in Java. It's not as easy as you might think!
The following code extract from that blog post describes "MediocreExecJava", a class that successfully runs a program using Runtime.exec() and manages its input and output without hanging. I've used it before and it works. I highly recommend reading the post to understand why!
import java.util.*;
import java.io.*;
public class MediocreExecJavac
{
public static void main(String args[])
{
try
{
Runtime rt = Runtime.getRuntime();
Process proc = rt.exec("javac");
InputStream stderr = proc.getErrorStream();
InputStreamReader isr = new InputStreamReader(stderr);
BufferedReader br = new BufferedReader(isr);
String line = null;
System.out.println("<ERROR>");
while ( (line = br.readLine()) != null)
System.out.println(line);
System.out.println("</ERROR>");
int exitVal = proc.waitFor();
System.out.println("Process exitValue: " + exitVal);
} catch (Throwable t)
{
t.printStackTrace();
}
}
}

Categories