I want to send some string to a child process and them, and this send me back in uppercase. The idea is that i put an empty string,both process will stop.
The first time works all great but the next times the process get stuck in the second time and i need to force close.
Father Code ->
public class padre {
public static void main(String[] args){
System.out.println("Soy el padre");
try {
Process p = Runtime.getRuntime().exec("java -jar C:\\Users\\Cristian\\Desktop\\hijo.jar");
InputStreamReader isr = new InputStreamReader(System.in);
BufferedReader escritorPadre = new BufferedReader(isr);
//Leer del hijo
BufferedReader brHijo = new BufferedReader(new InputStreamReader(p.getInputStream()));
BufferedReader brHijoError = new BufferedReader(new InputStreamReader(p.getErrorStream()));
//EScribir en el hijo
OutputStream os = p.getOutputStream();
String lineaConsola;
while((lineaConsola = escritorPadre.readLine()).isEmpty() == false){
lineaConsola +="\n";
os.write(lineaConsola.getBytes());
os.flush();
String linea;
while((linea = brHijo.readLine()) != null){
System.out.println(linea);
System.out.println("Atascado en el while del padre");
}
while((linea = brHijoError.readLine()) != null){
System.out.println(linea);
System.out.println("Atascado en el while del padre error");
}
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
Child code ->
public class hijo {
public static void main(String[] args){
InputStreamReader in = new InputStreamReader(System.in);
BufferedReader br = new BufferedReader(in);
String linea;
try {
while(!(linea = br.readLine()).isEmpty()){
System.out.println("Hijo -> " + linea.toUpperCase()+"\n");
System.out.println("Atascado en el while del hijo");
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
You have several problems here.
First, in your main loops (both father and son), you are not checking for end-of-file. This means that you can get null from the stream reader, and you'll get a NullPointerException when you try to access isEmpty().
Second, you are never passing an empty line to the son process, because as soon as the line you get from escritorPadre.readLine() is empty, you get out of the while loop. So the son checking for isEmpty() is futile. It would be better to close the stream to the son as soon as you exit (you always have to close streams when you finish with them, anyway), and handle the null on the son side.
But most importantly: your loop for reading from the son will always get stuck, because you will only get null from the son side on end-of-stream. And you will only get end-of-stream if the son uses close() on its output stream (or the son terminates). But if you did use close() on the son side, you'll not be able to write to standard output again.
So your loop just keeps waiting for the son to write something else, and the son is waiting for the father to write something else before it writes something. So the process is in a deadlock.
You could decide that the son should send some special output (an empty line) so you know when to stop reading its output.
But then you have a problem with standard error. After you finish reading from the son's output, you start reading from standard error. So to allow you to leave that loop, you'll also have to print an empty line to standard error after each write to the output, and also add an empty line to the stack trace being printed from every catch. Then you'll also have to add a check for empty line in the father's loop reading the son's standard error.
Personally, I think that's not very elegant. But it's doable.
Anyway, to get you started, on the father side you should make sure you close all the streams and check for null on reading. Something like:
try {
p = Runtime.getRuntime().exec("java -jar C:\\Users\\Cristian\\Desktop\\hijo.jar");
} catch ( IOException e ) {
e.printStackTrace();
return;
}
// Use try-with-resources to open all the streams and readers
// so that they will be closed automatically
try (
BufferedReader escritorPadre = new BufferedReader(new InputStreamReader(System.in));
//Leer del hijo
BufferedReader brHijo = new BufferedReader(new InputStreamReader(p.getInputStream()));
BufferedReader brHijoError = new BufferedReader(new InputStreamReader(p.getErrorStream()));
//EScribir en el hijo
OutputStream os = p.getOutputStream();
) {
String lineaConsola;
// Read until either end-of-file or empty line
while((lineaConsola = escritorPadre.readLine()) != null && ! lineaConsola.isEmpty()){
... // I ommitted the reading from the son until you decide how you want to arrange it.
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
And in the son, also, make sure to close all the resources or use try-with-resources, and check for both null and empty line:
try (
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
){
while((linea = br.readLine()) != null && ! linea.isEmpty()){
System.out.println("Hijo -> " + linea.toUpperCase()+"\n");
System.out.println("Atascado en el while del hijo");
// Decide how you want to handle telling the father that output is done
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Related
I am trying to invoke a command prompt and logging in some credentials into it. For this I am taking following approach:
public static void main(String[] args) throws InterruptedException {
// init shell
ProcessBuilder builder = new ProcessBuilder("cmd");
Scanner scanner = null;
BufferedWriter writer = null;
try {
scanner = new Scanner (System.in);
System.out.print("Enter your MS ID : ");
String user = scanner.nextLine();
System.out.print("Enter your MS Password : ");
String pass = scanner.nextLine();
Process p = builder.start();
writer = new BufferedWriter(new OutputStreamWriter(p.getOutputStream()));
writer.write("oc logout");
writer.newLine();
writer.flush();
writer.write("oc login <private-url>");
writer.newLine();
writer.flush();
writer.write(user);
writer.newLine();
writer.flush();
writer.write(pass);
writer.newLine();
writer.flush();
//Writing this will end the process after login is done
// writer.write("exit");
// writer.newLine();
// writer.flush();
Thread t = new Thread(new Runnable() {
public void run() {
try(BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream()))) {
;
String line;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
} catch (java.io.IOException e) {
e.printStackTrace();
}
}
});
t.setDaemon(true);
t.start();
p.waitFor();
} catch (IOException e) {
System.out.println(e);
}
finally {
scanner.close();
}
}
But when I run the program, it just does not end. Eclipse and running by cmd always need to explicitly exit the program. Maybe I am missing something. Any help would be greatly appreciated. It just works for the desired thing but does not exit and I need to integrate in my program and not able to do so.
You don't close the writer!
Your loop in the runnable task asks the reader whether it has more input:
while ((line = br.readLine()) != null) { ... }
This is always the case unless the writer in the main thread is closed. So for a quick solution put a simple writer.close() after the last writing action.
The better solution is to use the try-with-resources statement, introduced with Java 7. You should open your readers, writers, scanners, etc. as following:
try (Scanner scanner = ...) { ... }
try {Reader reader = ...) { ... }
try {Writer writer = ...) { ... }
These statements will handle the closing automatically for you. And as a side effect, it will make your code much more readable.
Warning: Closing a Scanner which is connected with System.in also closes the standard input, so that after that point no input can be read anymore. If that is not appropriate in your part of the code, then do not close the scanner.
I have to do this assignment but the thing says "All the programs should take input from STANDARD INPUT, not from a file." , this is fine to ask advice on, but what does this mean?
I was doing the code below but then someone said this is not correct, that its as if someone typed on the cmdline not a redirection from a file. I'm confused here, how do I do this so its stops when whoever tests my code gives it data, of while I don't know how they will other than stdin? Thanks, the data gets rather large like 500k.
List<Double> list = new ArrayList<Double>();
/*
* Get the data from the stdin and read into a buffer until its done.
*/
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
String s;
try {
while ((s = in.readLine()) != null && s.length() != 0) {
double myReal = Double.parseDouble(s);
// System.out.println(myReal);
list.add(myReal);
// System.out.println(s);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
I'm still learning Java and I need some help understanding why this code is wrong:
BufferedReader infile = new BufferedReader(new FileReader(file));
String regel = infile.readLine();
while (regel != null) {
// Do something with regel.
regel = infile.readLine();
}
infile.close();
I really don't see the problem but Eclipse keeps telling there is a resource leak and that infile isn't closed.
(one more detail, this code stands in a try block but I left it away to keep it simple)
Eclipse is complaining because the reference may not be closed (for example, in an Exception); this is where you would use a finally block - perhaps like so
BufferedReader infile = null;
try {
infile = new BufferedReader(new FileReader(file));
String regel = infile.readLine();
while (regel != null) {
// Do something with regel.
regel = infile.readLine();
}
} catch (Exception e) {
e.printStackTrace(); // Log the exception.
} finally {
if (infile != null) {
infile.close(); // close the resource.
}
}
You should have a try/catch block.
Also you should use the following instead:
while ((line = reader.readLine()) != null) {
//do something with line;
}
I think Elliott Frisch is correct and pointed out the main reason the only thing I would add is You should close the stream (in a finally block) because to ensure that any output buffers are flushed in the case that output was otherwise successful. If the flush fails, the code should exit via an exception. Here is another example similar to what you are trying to solve and make sure you look at (Guideline 1-2: Release resources in all cases) http://www.oracle.com/technetwork/java/seccodeguide-139067.html
final OutputStream rawOut = new FileOutputStream(file);
try {
final BufferedOutputStream out =
new BufferedOutputStream(rawOut);
use(out);
out.flush();
} finally {
rawOut.close();
}
Can someone help me in the below scenario,
I need to call a perl script from my java code. The perl script is an interactive code, which gets the input from the user during its execution and continues further to end. So, the example I have used is, the perl script when executed asks for the age by printing in the console "How old are you?", when the user enter some value say '26'. Then it prints "WOW! You are 26 years old!".
When I tried calling this script from my java code, the process waits till I give the value as 26 in the outputstream, while in the inputstream there is no value. Then finally when again I read the inputstream, i get the entire output of the script together. So, here can't I make it interactive?
I have went through many forums and blogs, but couldn't locate any, which exactly target my requirement.
Here is the java code
import java.io.*;
public class InvokePerlScript {
/**
* #param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Process process;
try
{
process = Runtime.getRuntime().exec("cmd /c perl D:\\sudarsan\\eclips~1\\FirstProject\\Command.pl");
try {
BufferedReader in = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line = null;
while ((line = in.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
try {
BufferedWriter out = new BufferedWriter(new OutputStreamWriter(process.getOutputStream()));
out.write("23");
out.flush();
out.close();
} catch (IOException e) {
e.printStackTrace();
}
process.waitFor();
if(process.exitValue() == 0)
{
System.out.println("Command Successful");
try {
BufferedReader in = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line = null;
while ((line = in.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
}
else
{
System.out.println("Command Failure");
try {
BufferedReader in = new BufferedReader(new InputStreamReader(process.getErrorStream()));
String line = null;
while ((line = in.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
catch(Exception e)
{
System.out.println("Exception: "+ e.toString());
}
}
}
Perl code is as below
$| = 1;
print "How old are you? \n";
$age = <>;
print "WOW! You are $age years old!";
Thanks in advance,
Sudarsan
Are you calling flush() on the OutputStream in Java after writing the values? If you don't, there's a good chance they'll just be held in the stream's buffer within the Java process, and so never make it to Perl (with the result that both processes end up waiting for the other's IO.)
(Depending on the implementation of the stream this may or may not be necessary, but it certainly wouldn't hurt - and I've been bitten by this in the past. Usually one doesn't need to be as careful, since flushing happens implicitly when close() is called, but here you can't close the stream after you've finished writing.)
It looks like you're trying to read a full line in this code:
BufferedReader in = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line = null;
while ((line = in.readLine()) != null) {
...
However, in your perl code, you are not printing an endline character, so readLine never returns (as per the documentation).
Here is a simple piece of code:
import java.io.*;
public class Read {
public static void main(String[] args) {
BufferedReader f = new BufferedReader(new InputStreamReader(System.in));
while(true)
{
String x = null;
try{
x = f.readLine();
}
catch (IOException e) {e.printStackTrace();}
System.out.println(x);
}
}
}
I execute this as : java Read < input.txt
Once the input.txt is completely piped into the program, x keeps getting infinite nulls. Why is that so? Is there a way by which I can make the Standard In(Command Line) active after the file being fed into the code is done?
I've tried closing the stream and reopening, it doesn't work. Reset etc also.
By executing "java Read < input.txt" you've told the operating system that for this process, the piped file is standard in. You can't then switch back to the command line from inside the application.
If you want to do that, then pass input.txt as a file name parameter to the application, open/read/close the file yourself from inside the application, then read from standard input to get stuff from the command line.
Well, this is typical for reading in a BufferedReader. readLine() returns null when end of stream is hit. Perhaps your infinite loop is the problem ;-)
// try / catch ommitted
String x = null;
while( (x = f.readLine()) != null )
{
System.out.println(x);
}
You need to terminate your while loop when the line is null, like this:
BufferedReader in = null;
try {
in = new BufferedReader(new InputStreamReader(System.in));
String line;
while ((line = in.readLine()) != null) {
System.out.println(line);
}
}
catch (IOException e) {
logger.error("IOException reading System.in", e);
throw e;
}
finally {
if (in != null) {
in.close();
}
}
Is there a way by which I can make the Standard In(Command Line)
active after the file being fed into the code is done?
Sorry to bump an old question, but none of the answers so far points out that there is a (shell-only) way to pass back to console input after piping in a file.
If you run the command
{ cat input.txt & cat; } | java Read
then the text from input.txt will be passed to java Read and you will then be dropped back to console input.
Note that if you then press Ctrl+D, you will get the infinite loop of nulls, unless you modify your program to end the loop when it receives null.