calling a perl script from a java code in a interactive way - java

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

Related

Running python with command line in java

The program is stuck at p2.waitFor() (I tested with printing strings before and after)
public void score() {
this.toXML();
try {
Process p = Runtime
.getRuntime()
.exec("python sumocfg_maker.py Carrefour.net.xml Detectors.det.xml edgedata.csv -ef");
p.waitFor();
Process p2 = Runtime.getRuntime().exec("python simulation.py");
new Thread(new Runnable() {
public void run() {
BufferedReader input = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line = null;
try {
while ((line = input.readLine()) != null)
System.out.println(line);
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
p2.waitFor();
} catch (Exception e) {
e.printStackTrace();
}
}
and simulation.py is
import os
os.system('cmd /c "sumo -c Simulation.sumocfg --duration-log.statistics --log duration.txt)
The simulation.py runs fine on its own. When I put the command in simulation.py in java, I get the same problem.
The System.out.println(line); prints out "Success" and then nothing
I left out code from simulation.py that saves a file that the java reads right after the p2.wait(), and without the p2.wait() the file never changes.
You have
BufferedReader input = new BufferedReader(new InputStreamReader(p.getInputStream()));
But you need
BufferedReader input = new BufferedReader(new InputStreamReader(p2.getInputStream()));
since p is already finished, the bufferedreader will wait but never receive anything.

While-Loop not exiting when reading command

I have a problem when using while-loops to read the text a command returns.
I have the following code:
class CommandExecuter {
ProcessBuilder builder = null;
Process p = null;
BufferedWriter writer = null;
String str = "";
BufferedReader hey = null;
StringBuffer buffer = null;
CommandExecuter() {
}
public void run() {
builder = new ProcessBuilder("/bin/bash");
try {
p = builder.start();
}
catch (IOException e) {
e.printStackTrace();
}
writer = new BufferedWriter(new OutputStreamWriter(p.getOutputStream()));
hey = new BufferedReader(new InputStreamReader(p.getInputStream()));
buffer = new StringBuffer();
}
String exec(String command) {
try {
writer.write(command);
writer.newLine();
writer.flush();
int a = 0;
while ((str = hey.readLine()) != null) {
a++;
System.out.println(a);
buffer = buffer.append(str + ":");
}
System.out.println("finished");
return buffer.toString();
}
catch (Exception e) {
e.printStackTrace();
}
return "1";
}
}
The problem is, the while loop is doesn't when the console finished printing text. It never reaches the part where it prints "finished". When using the command ls, it return a 8 lines long String. The variable "a" prints till it reaches 8, and stops, but the loop does not exit because it never reaches the finished part.
You are running an interactive shell and feeding it commands through standard input; once a command is executed, the shell will print its output and keep running, waiting for more commands. Therefore, standard output of the shell process will never be closed.
What you should probably do instead is run each command as its own, new, process, possibly as /bin/bash -c command.

Java and stdin vs a file

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

Still resource leak after closing BufferedReader

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

Java Process cannot get the InputStream through Runtime.getRunTime().exec()

try {
String str;
Process process = Runtime.getRuntime().exec("bash /home/abhishek/workspace/Pro/run");
InputStream isout = process.getInputStream();
InputStreamReader isoutr = new InputStreamReader(isout);
BufferedReader brout = new BufferedReader(isoutr);
while ((str = brout.readLine()) != null) {
System.out.println(str);
}
} catch (IOException e) {
e.printStackTrace();
}
The Code has issues with getting the InputStream from the Process,
because if I run the Shell script from my Terminal it runs completely fine,
but if I Run the Script like this,the str is always null,
I am using this code to get the output of the Shell Script directly into Java instead writing the Script Output in the File
Is there any other way to achieve this,or how can I get the issue solved using the current approach
I think something returned through the error stream, so you can try to check something from the Process.getErrorStream().
You should also wait for the created process to prevent your main program completes before it. Use Process.waitFor();
public class TestMain {
private static final String BASH_CMD = "bash";
private static final String PROG = "/home/abhishek/workspace/Pro/run";
private static final String[] CMD_ARRAY = { BASH_CMD , PROG };
public static void main(String[] args) {
new Thread(new Runnable() {
public void run() {
BufferedReader reader = new BufferedReader(new InputStreamReader(
System.in));
String command = null;
try {
while ((command = reader.readLine()) != null) {
System.out.println("Command Received:" + command);
}
} catch (Exception ex) {
ex.printStackTrace();
// failed to listening command
}
}
}).start();
Process process = null;
try {
ProcessBuilder processBuilder = new ProcessBuilder(CMD_ARRAY);
process = processBuilder.start();
InputStream inputStream = process.getInputStream();
setUpStreamGobbler(inputStream, System.out);
InputStream errorStream = process.getErrorStream();
setUpStreamGobbler(errorStream, System.err);
System.out.println("never returns");
process.waitFor();
} catch (IOException e) {
throw new RuntimeException(e);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
public static void setUpStreamGobbler(final InputStream is, final PrintStream ps) {
final InputStreamReader streamReader = new InputStreamReader(is);
new Thread(new Runnable() {
public void run() {
BufferedReader br = new BufferedReader(streamReader);
String line = null;
try {
while ((line = br.readLine()) != null) {
ps.println("process stream: " + line);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}).start();
}
}
Edit you /home/abhishek/workspace/Pro/run if it is a shell and add the following line on top.
#!/usr/bin/bash
and give required execute permissions to /home/abhishek/workspace/Pro/run.
Then use the following line
Process process = Runtime.getRuntime().exec("/home/abhishek/workspace/Pro/run");
Now if the run program prints anything you should see it in the output.
Your code looks fine. So, I believe that problem is either in command line you are using (bash /home/abhishek/workspace/Pro/run) or in your script itself.
I'd suggest you to perform the following steps:
try to run some well-known command instead of your script. For example pwd. Check that your code that is reading from input stream works correctly.
Now try to simplify your script. Create script run1 that just runs the same pwd. Now run this script from java and see that it is working. BTW you do not have to run it as bash yourscript. You can directly run it without bash prefix
If all this works start to move from simple to your real script step-by-step. I believe you will find your mistake. Probably your script cannot start for some environment related problems.
Possible problem is by the time you obtain inputStram the sub-process is not ready
Try
Process process = Runtime.getRuntime().exec("bash /home/abhishek/workspace/Pro/run");
InputStream isout = process.getInputStream();
process.waitFor()
Try something like this:
String[] runCommand = new String[3];
runCommand[0] = "sh";
runCommand[1] = "-c";
runCommand[2] = "bash /home/abhishek/workspace/Pro/run";
Process process = runtime.exec(runCommand);
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line = reader.readLine();
reader.close();
After multiple readings of the source code for the unix implementation of Process at https://github.com/AdoptOpenJDK/openjdk-jdk11/blob/master/src/java.base/unix/classes/java/lang/ProcessImpl.java it seems that the standard redirects will always swallow to a ProcessBuilder.NullInputStream:
if (redirects[1] == Redirect.PIPE) {
std_fds[1] = -1;
}...
and
stdout = (fds[1] == -1 || forceNullOutputStream) ?
ProcessBuilder.NullInputStream.INSTANCE :
new ProcessPipeInputStream(fds[1]);
(The same code repeats for stdIn, stdOut and stdErr streams)
The only workaround I have found, which feels very clumsy is to use a temp File:
File stdOutTmp; // create and destroy however you see fit
ProcessBuilder pb = ...;
pb.redirectOutput(ProcessBuilder.Redirect.to(stdOutTmp));
...
There are other static factory methods (Redirect.appendTo(File) to append to an existing file rather than overwrite an existing file, and Redirect.from(File) for stdIn)

Categories

Resources