How to write output of child process in Java - java

I have written a java code in Eclipse and i am developing a plug-in which embed a button on workbench. When this button is clicked, it opens a batch file (located in c:/program file/prism 4.0/bin)
The code successfully opens the .bat file ! But my next task is write the output of that batch file on my console. I am using Eclipse IDE version 3.
My code is
MessageConsoleStream out = myConsole.newMessageStream();
out.println("We are on console ! \n Shubham performed action");
try {
ProcessBuilder pb=new ProcessBuilder("C:\\Program Files\\prism-4.0\\bin\\prism.bat");
pb.directory(new File("C:\\Program Files\\prism-4.0\\bin"));
Process p=pb.start();
int exitVal=p.waitFor();
out.println("Exited with error code "+exitVal+" shown and action performed \n");
out.println("Shubham Process Successful");
out.println("Printing on console");
}
catch (Exception e)
{
out.println(e.toString());
e.printStackTrace();
}
}

Do like this:
.....
Process p = pb.start();
BufferedReader input = new BufferedReader(new InputStreamReader(p.getInputStream()));
String in;
while((in = input.readLine()) != null) {
out.println(in);
}
int exitVal = p.waitFor();
.....
Note that if the batch file writes to standard error your java program must consume it otherwise the p.waitFor() will never return.

Do yourself a big favor and check http://commons.apache.org/exec/. It will take care of all the awful details about managnig an external process: timeout, handling input/output, even creating the command line will be easier and less error prone

Note that to correctly read from the InputStreams of a Process, you should do so on separate Threads. See this similar question.

Related

Running a .exe file from a Java web app doesn't work

I'm trying to run a .exe file develop in Pascal from my web app(Windows + Primefaces 5 + Tomcat 8). The program generate a text file that I'm gonna read it after but it seems it doens't have the permission to do that, no exceptions were threw.
Here is how I pick the path:
String path = FacesContext.getCurrentInstance().getExternalContext().getRealPath("/WEB-INF/lib/");
projeto.setQtde(1);
this.esquemas = gerenciarProjeto.realizarCorte(projeto, usuario, path+"/");
and here is how I call the program:
Runtime rt = Runtime.getRuntime();
Process pc = rt.exec(this.caminho+"cortebi.exe");
InputStreamReader isr = new InputStreamReader(pc.getErrorStream());
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 = pc.waitFor();
System.out.println("Process exitValue: " + exitVal);
I realized that if I put the .exe file into my project root and run Eclipse as administrator it works. But I do not know how to put it into my web app root to do the same after it's deployed, I've tried to put it in diferent locations and nothing!
I was able to figure out what was the problem. I tought that Runtime.getRuntime().exec() would behave like a tradicional DOS prompt but I was wrong. In fact the problem itself was not a windows or file permission problem but a misconception of how the exec() method works. I wrote a new piece of code:
public void executarCortebi(File file){
try {
Process pc = Runtime.getRuntime().exec("cmd /c start cortebi.exe",null, file);
StreamGobbler error = new StreamGobbler(pc.getErrorStream(), "ERRO");
StreamGobbler output = new StreamGobbler(pc.getInputStream(), "OUTPUT");
error.start();
output.start();
pc.waitFor();
Thread.sleep(800);
} catch (IOException e) {
e.printStackTrace();
}catch(InterruptedException e) {
e.printStackTrace();
}
}
As I said the way the exec() method works is more restricted I found good material here:When Runtime.exec() won't.
Explaining the parameters of getRuntime.exec() used for me, we have the fallow: First the command itself that must be executed(It's noticeable the diferences between the old cold and the new one), second are the arguments for the .exe file wich in this case are none and third is the file that has the path from my .exe program.
Now everything is working!

How to execute a interactive shell script using java Runtime?

I am wondering is there any way to execute following shell script, which waits for user input using java's Runtime class?
#!/bin/bash
echo "Please enter your name:"
read name
echo "Welcome $name"
I am using following java code to do this task but it just shows blank console.
public class TestShellScript {
public static void main(String[] args) {
File wd = new File("/mnt/client/");
System.out.println("Working Directory: " +wd);
Process proc = null;
try {
proc = Runtime.getRuntime().exec("sudo ./test.sh", null, wd);
} catch (Exception e) {
e.printStackTrace();
}
}
}
Thing is when I execute above program, I believed it will execute a shell script and that shell script will wait for user input, but it just prints current directory and then exits. Is there any way to do this or it is not possible at all in java?
Thanks in advance
The reason it prints the current dir and exits is because your java app exits. You need to add a (threaded) listener to the input and error streams of your created process, and you'll probably want to add a printStream to the process's output stream
example:
proc = Runtime.getRuntime().exec(cmds);
PrintStream pw = new PrintStream(proc.getOutputStream());
FetcherListener fl = new FetcherListener() {
#Override
public void fetchedMore(byte[] buf, int start, int end) {
textOut.println(new String(buf, start, end - start));
}
#Override
public void fetchedAll(byte[] buf) {
}
};
IOUtils.loadDataASync(proc.getInputStream(), fl);
IOUtils.loadDataASync(proc.getErrorStream(), fl);
String home = System.getProperty("user.home");
//System.out.println("home: " + home);
String profile = IOUtils.loadTextFile(new File(home + "/.profile"));
pw.println(profile);
pw.flush();
To run this, you will need to download my sourceforge project: http://tus.sourceforge.net/ but hopefully the code snippet is instructive enough that you can just adapt to J2SE and whatever else you are using.
If you use a Java ProcessBuilder you should be able to get the Input, Error and Output streams of the Process you create.
These streams can be used to get information coming out of the process (like prompts for input) but they can also be written to to put information into the process directly too. For instance:
InputStream stdout = process.getInputStream ();
BufferedReader reader = new BufferedReader (new InputStreamReader(stdout));
String line;
while(true){
line = reader.readLine();
//...
That'll get you the output from the process directly. I've not done it myself, but I'm pretty sure that process.getOutputStream() gives you something that can be written to directly to send input to the process.
The problem with running interactive programs, such as sudo, from Runtime.exec is that it attaches their stdin and stdout to pipes rather than the console device they need. You can make it work by redirecting the input and output to /dev/tty.
You can achieve the same behaviour using the new ProcessBuilder class, setting up the redirection using ProcessBuilder.Redirect.INHERIT.
Note sure at all you can send input to your script from Java. However I very strongly recommend to have a look at Commons Exec if you are to execute external scripts from Java:
Commons Exec homepage
Commons Exec API

running linux copy and rename command with java

File wd = new File("/bin");
Process proc = null;
try {
proc = Runtime.getRuntime().exec("/bin/bash", null, wd);
} catch (IOException e) {
logger.info(e);
e.printStackTrace();
}
if (proc != null) {
BufferedReader in = new BufferedReader(new InputStreamReader(proc.getInputStream()));
PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(proc.getOutputStream())), true);
//out.println("su - root");
out.println("cp /usr/rock/Masterfile.xls /usr/rock/generatedfile/");
out.println("mv /usr/rock/generatedfile/Masterfile.xls /usr/rock/generatedfile/userid.xls");
try {
String line;
while ((line = in.readLine()) != null) {
logger.info(line);
}
proc.waitFor();
in.close();
out.close();
proc.destroy();
} catch (Exception e) {
logger.info(e);
e.printStackTrace();
}
}
I am trying to copy master file and want to rename according to the userid. Code does not showing any error but i dont see any file in the folder i specify. I tried with sudo root command even its not copying and renaming the file. How should i do in order to run copy and rename command to run successfully from java program.
You're not reading from the process's standard error. So if your cp and mv commands are reporting errors, you won't be seeing them.
It's possible to read from the process's standard error, but that's complicated if you're using Runtime.getRuntime().exec() because reading from standard error needs to be done in a separate thread to reading from standard output.
Java 5 introduced a new class for running external processes: ProcessBuilder. In my opinion, the single biggest advantage of a ProcessBuilder is that you can redirect the standard error of the process into its standard output. That leaves you with only one stream to read from, and hence no need for a separate thread.
I would recommend replacing your use of Runtime.getRuntime().exec(...) with the following:
ProcessBuilder builder = new ProcessBuilder("/bin/bash");
builder.directory(wd);
builder.redirectErrorStream(true);
proc = builder.start();
If the files aren't being copied, then chances are that cp and mv are reporting errors. Making this change should hopefully allow you to see the errors being reported.

in java,getting error "Cannot run program "/bin/sh": java.io.IOException: error=24, Too many open files"

I am using the following code to run the shell script continuously.
String[] process = new String[] {"/bin/sh", "-c","pgrep httpd" };
Process proc = new ProcessBuilder(process).start();
InputStreamReader input = new InputStreamReader(proc
.getInputStream());
BufferedReader reader = new BufferedReader(input);
String line = reader.readLine();
reader.close();
input.close();
When run this code in thread, I am getting the error message
MESSAGE: Too many open files
java.net.SocketException: Too many open files
and
Cannot run program "/bin/sh": java.io.IOException: error=24, Too many open files.
How to avoid this issue .
This can occur due to a number of reasons:
There might be a limit on the number of files you are allowed to open. You may need to raise the number of open files you are allowed in the /etc/security/limits.conf file.
if you are running this continuously in a loop then it may result in spwanning of large number of processes.You probably want to int exitValue = p.waitFor() to wait for the process to complete.
try the following pattern and see what it happens:
try {
String[] process = new String[]{"/bin/sh", "-c", "pgrep httpd"};
Process proc = new ProcessBuilder(process).start();
InputStreamReader input = new InputStreamReader(proc.getInputStream());
BufferedReader reader = new BufferedReader(input);
String line = reader.readLine();
int rc = proc.waitFor();
reader.close();
input.close();
} catch (IOException e) {
e.printStackTrace(); // or log it, or otherwise handle it
} catch (InterruptedException ie) {
ie.printStackTrace(); // or log it, or otherwise handle it
}
It is system proble try google. "linux too many open files"
You must increase value, which specify how many files can be opened at once (in your operating system)
you will probably find something like "/proc/sys/fs/file-max"

Runtime exec and a custom built RTF editor

I have a class that manages the creation of RTF documents and a method in that class that calls the RTF editor with a XML file for display.
All but one user can access this editor without any issues. This one user consistently runs into an issue where their application just hangs. There are no errors in any logs. Normally this kind of problem is easily identified, reproduced and corrected, however, I can't for the life of my reproduce it so my attempts at debugging are failing.
Basically the code is as follows:
int exitVal = CUBSRTFEditor.runRTFEditor("c:\\tmp\\control"+ap_doc_id+".xml", xml,"I:\\AppealsLetters.exe /process \"c:\\tmp\\control"+ap_doc_id+".xml\"");
public static int runRTFEditor(String xmlLocation, String xmlContent, String executePath)
{
int exitVal = 0;
createLocalFile(xmlLocation, xmlContent);
try
{
System.out.println("executePath must = "+executePath);
Runtime rt = Runtime.getRuntime();
Process proc = rt.exec(executePath);
System.out.println("after executePath runs");
//exhaust that stream before waiting for the process to exit
InputStream inputstream = proc.getInputStream();
InputStreamReader inputstreamreader = new InputStreamReader(inputstream);
BufferedReader bufferedreader = new BufferedReader(inputstreamreader);
// read the ls output
String line;
while ((line = bufferedreader.readLine())!= null)
{
System.out.println(line);
}
exitVal = proc.waitFor();
}
catch (Throwable t)
{
t.printStackTrace();
}
CUBSRTFEditor.deleteTempFile(xmlLocation);
return exitVal;
}
The last output is the first System.out. When I take the xml file and execute this on any other PC it executes without issue. I see no useful info in proc.getErrorStream() or proc.getOutputStream().
The JDK's Javadoc documentation on this problem (exec hanging):
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 subprocess may cause the subprocess to block, and even deadlock.
I try exhausting that stream before waiting for the process to exit and that does not seem to help as it never seems to get to that point (the second System.out is not displayed)
Have I implemented this incorrectly? Am I missing something important? Any ideas on how to get more info out of the process would be great.
I am stuck....
Runtime.exec() is a deceptively nasty little spud to work with. I found this article (old, but still relevant) to be quite helpful. You can always skip to Page 4 for some highly gankable sample code. :-)
At a glance, your code needs to handle both proc.getOutputStream() and proc.getErrorStream(), which is a good reason to handle those streams in separate threads.
I wanted to update this because the change went into Production today and worked. Based off of BlairHippo's suggestions I got it to work with an anonymous inner class to create a separate thread to exhaust both the Error and Input streams.
new Thread(new Runnable(){
public void run()
{
try
{
BufferedReader br = new BufferedReader(new InputStreamReader(proc.getErrorStream()));
String line;
while ((line = br.readLine())!= null)
{
System.out.println(line);
}
}
catch (Throwable t)
{
t.printStackTrace();
}
}
}).start();

Categories

Resources