Java ProcessBuilder issue - java

I am having trouble using the > operator to write to file in Process Builder. The actual process will run fine and let me parse using a InputStreamReader etc to parse the output. I want the process to be written to file using command line like >test.json for example.
Here is my code
try {
//process builder used to run a Python script
ProcessBuilder process = new ProcessBuilder("python",
System.getProperty("user.dir")+"\\createJson.py","--structure","cloc.csv",
"--weights","EntityEffort.csv",">","a.json");
process.directory(new File("c:\\users\\chris\\desktop\\test2"));
Process p = process.start();
} catch(Exception e) {
e.printStackTrace();
}

As #JimGarrison points out, > is interpreted by the shell. Here you are directly starting a process for createJson.py, without a shell.
In UNIX you could use ProcessBuilder to start a shell using:
process = new ProcessBuilder("bash", "someCommand", ">", "outputfile");
Something similar will probably work with Windows and cmd.exe.
However, it's not very good practice. It's an opportunity for command injection attacks. Calling external processes is a last-resort approach, and you should try and minimise what you do within those processes.
So you would be better off sticking with what you have, and handle the redirect to file in Java. The ProcessBuilder javadoc gives an example:
File log = new File("log");
processBuilder.redirectOutput(Redirect.appendTo(log));

Related

Java Process Builder not executing multiple commands

Hi a Java newbie here.
I am currently building a Java application that executes multiple linux commands with Java Process builder.
I am planning to execute a shell scipt, and since it this shell script is an external program that takes about a second to fully execute, let the process sleep for a second and write the result into a txt file.
This is an external program and it must take in "q" to exit the program, so I finally need to type in q in the terminal.
I have gotten help on this community before and the code I constructed with that help is as follows.
public static void Linux(String fileName){
try {
File dir = new File("/usr/local/bin");
ProcessBuilder pb = new ProcessBuilder(new String[]{"/bin/bash", "-c", "./test_elanprv2.2.sh > \"$1\"; sleep 1; q", "_", fileName + ".txt"});
System.out.println("wrote text");
pb.directory(dir);
Process start = pb.start();
start.destroy();
}catch (Exception e){
e.printStackTrace();
System.out.println("failed to write text");
}
The process builder does create a txt file but it seems to be empty, and no matter how long I set the sleep to, the programs seems to instanly return the print statement.
It would really be appreciated if anyone could tell me what I am doing wrong.
Thank you in advance!!
As mentioned by #VGR, try using redirectOutput
public static void Linux(String fileName){
try {
File dir = new File("/usr/local/bin");
ProcessBuilder pb = new ProcessBuilder(new String[]{"/bin/bash", "-c", "./test_elanprv2.2.sh");
File output = new File("_", fileName + ".txt");
pb.redirectOutput(output);
System.out.println("wrote text");
pb.directory(dir);
Process start = pb.start();
start.destroy();
} catch (Exception e) {
e.printStackTrace();
System.out.println("failed to write text");
}
Most of your issues are due to a misunderstanding of how processes work. These concepts are not Java concepts; you would have the same issues in any other language.
First, you are destroying your process before it runs, and possibly before it even gets started. This is because pb.start() starts the process, and then you immediately destroy it without giving it a chance to complete.
You shouldn’t need to destroy the process at all. Just let it finish:
Process start = pb.start();
start.waitFor();
All processes have their own standard input and standard output. Again, this is not a Java concept; this has been a fundamental feature in Unix and Windows operating systems for a long time.
Normally, when a process prints information by writing it to its standard output. That is in fact what Java’s System.out.println does. In Unix shells (and in Windows), the > character redirects the standard output of a process to a file; the program still writes to its standard output, without ever knowing that the operating system is sending that output to a destination other than the terminal. Since it’s a fundamental operating system function, Java can do it for you:
ProcessBuilder pb =
new ProcessBuilder("/bin/bash", "-c", "./test_elanprv2.2.sh");
pb.redirectOutput(new File(fileName + ".txt"));
Similarly, when a process wants to take input, it normally does so by reading from its standard input. This is not the same as executing another command. When you do this:
./test_elanprv2.2.sh > "$1"; sleep 1; q
You are not sending q to the shell script. The above commands wait for the shell script to finish, then execute a sleep, then try to execute a program named q (which probably doesn’t exist).
Since the test_elanprv2.2.sh shell script probably accepts commands by reading its standard input, you want to send the q command to the standard input of that process:
ProcessBuilder pb =
new ProcessBuilder("/bin/bash", "-c", "./test_elanprv2.2.sh");
pb.redirectOutput(new File(fileName + ".txt"));
Process start = pb.start();
Thread.sleep(1000);
try (Writer commands = new OutputStreamWriter(start.getOutputStream())) {
commands.write("q\n");
}
// Caution: Call this AFTER writing commands. You don't want to write
// to the standard input of a process that has already finished!
start.waitFor();

Run batch file code from java code

I want to execute a batch file code from java button click. Also I don't want any command prompt window to be shown all from java code.
I have a code :-
C:\xyz-3.1.1\bin>dita --input=C:/Users/india/Desktop/mobile-phone/m
obilePhone.xyz --format=pdf --output=C:/Users/india/Desktop --logfile=C:/Use
rs/india/Desktop/dofhdif.txt
So I want above code to be run from batch command with C:\xyz-3.1.1\bin> as the parent directory.
Also I want to update --input file path whenever I will choose new file from JFileChooser.
I did this from the java code on button click transform:-
ProcessBuilder pb=new ProcessBuilder("dita --input=C:/Users/india/Desktop/mobile-phone/mobilePhone.xyz --format=pdf --output=C:/Users/india/Desktop --logfile=C:/Users/india/Desktop/dofhdif.txt");
pb.redirectErrorStream(true);
Process process=pb.start();
and getting IOException error.
I get stuck over here for long time , where am I going wrong.
EDIT :- error
java.io.IOException: Cannot run program "dita --input=C:/Users/india/Desktop/mobile-phone/m
obilePhone.xyz --format=pdf --output=C:/Users/india/Desktop --logfile=C:/Use
rs/india/Desktop/dofhdif.txt": CreateProcess error=2, The system cannot find the file specified
at java.lang.ProcessBuilder.start(Unknown Source)
As the Error mentioned, it cannot locate the command because the whole string will be treated as the command by ProcessBuilder.
Try to use Runtime.getRuntime().exec directly, but you have to ensure the command dita can be found.
Process process = Runtime.getRuntime().exec("C:\xyz-3.1.1\bin>dita --input=C:/Users/india/Desktop/mobile-phone/mobilePhone.xyz --format=pdf --output=C:/Users/india/Desktop --logfile=C:/Users/india/Desktop/dofhdif.txt");
process.waitFor();
int exitCode = process.exitValue();
System.out.println(IoHelper.output(process.getInputStream())); // handle the output;
Before JDK 5.0, the only way to start a process and execute it, was to use the exec() method of the java.lang.Runtime class after which ProcessBuilder can be used to help create operating system processes.
The major improvement being that, it also acts as a holder for all those attributes that influence the process. And this is how it should be used:
ProcessBuilder pb = new ProcessBuilder("myCommand", "myArg1", "myArg2");
P.S. Actually Runtime.getRuntime().exec can also be used with String... as:
Runtime.getRuntime().exec(new String[]{"curl", "-v", "--cookie", tokenString, urlString});
My personal preference:
If you have to configure the environment for the command: to control the working directory or environment variables and also you want to execute the commands several times, you'd better use it since the ProcessBuilder will hold the settings and what you need to do is just processBuilder.start() to create another process with the same settings;
If you want to execute a whole long string command as you mentioned, you'd better just use Runtime.getRuntime().exec since you can just execute it right there without any bothering of the parameter format.
Try this:
String inputFile = ...;
String outputFile = ...;
String logFile = ...;
ProcessBuilder pb = new ProcessBuilder(
"dita",
"--input=" + inputFile,
"--format=pdf",
"--output=" + outputFile,
"--logfile=" + logFile)
.directory(new File("C:\\xyz-3.1.1\\bin"))
//.inheritIO();
.redirectErrorStream(true);
Process process = pb.start();
This shows the following points:
The command is separated from the arguments
The argument values can be determined at runtime
The command's default directory (C:\xyz-3.1.1\bin) is set before starting the process
Consider using inheritIO() instead of redirectErrorStream() if you want the process's output to appear as part of your Java application's output.

Calling Python from Java (Tomcat6) as sub-process

I am trying to call a python script from a java/tomcat6 webapp. I am currently using the following code:
Process p = Runtime.getRuntime().exec("python <file.py>");
InputStream in = p.getInputStream();
InputStreamReader isr = new InputStreamReader(in);
BufferedReader b = new BufferedReader(isr);
logger.info("PYTHON OUTPUT");
String line = null;
while ( (line = b.readLine()) != null){
logger.info(line);
}
p.waitFor();
logger.info("COMPLETE PYTHON OUTPUT");
logger.info("EXIT VALUE: "+p.exitValue());
I can't really see any output in the catalinia.out file from the python script and using an adapter library like jython is not possible as the script relies on several machine learning libraries that need python's Numpy module to work.
Help?
The explanation is probably one (or more) of following:
The command is failing and writing error messages to its "stderr" fd ... which you are not looking at.
The command is failing to launch because the command name is incorrect; e.g. it can't be found on $PATH.
The command is trying to read from its stdin fd ... but you haven't provided any input (yet).
It could be a problem with command-line splitting; e.g if you are using pathnames with embedded spaces, or other things that would normally be handled by the shell.
Also, since this is python, this could be a problem with python-specific environment variables, the current directory and/or the effective user that is executing the command.
How to proceed:
Determine if the python command is actually starting. For instance. "hack" the "" to write something to a temporary file on startup.
Change to using ProcessBuilder to create the Process object. This will give you more control over the streams and how they are handled.
Find out what is going to the child processes "stderr". (ProcessBuilder allows you to redirect it to "stdout" ...)

Cannot run exe file with ProcessBuilder in Java

I am trying to run an exe file while setting some parameters for it like this:
myExePath -ini myIniPath -x myConfigFilePath
When I run it from the command line it works perfectly. But when I try running it from my Java code the process starts but after a while is not responding anymore so I have to forcibly close it. I am using this Java code:
List<String> parameters = new ArrayList<String>();
parameters.add(myexePath);
parameters.add("-ini ");
parameters.add(myIniPath);
parameters.add("-x ");
parameters.add(myConfigPath
ProcessBuilder builder = new ProcessBuilder(parameters);
Process process = builder.start();
try {
process.waitFor();
} catch (InterruptedException e) {
System.err.println("Process was interrupted");
}
Any ideas what I am doing wrong?
Does the exe use stdout, stderr, stdin? You should always read from them or close them. Depending on the implementation and buffer size not reading from them could lead to blocking.
I'm not sure if it helps, but why you use spaces?
e.g.: parameters.add("-x ");
You don't need them.
What you can also try is to put all your parameters in an array and use another constructor of ProcessBuilder which takes an array as argument.
I guess you should first get a reference to the Runtime.
You could do this
Runtime.getRuntime().exec(parameters.toString());
Your string from the parameters list may need a bit formatting.

How can i run a .jar file in java

I'm making an update function for my project, it's working great, until i want it to restart, basically I download the new file and replace it with the old one, and then i want to run it again, now for some reason it doesn't wna run, and i don't get any error...
Here is the complete update class:
http://dl.dropbox.com/u/38414202/Update.txt
Here is the method i'm using to run my .jar file:
String currDir = new File("(CoN).jar").getAbsolutePath();
Process runManager = Runtime.getRuntime().exec("java -jar " + currDir);
It's not clear to me, why do you need to run the jar with a call to exec() . Given that you need to run the code in the .jar file from a Java program, you could simply run the main() method as defined in the jar's manifest, and capture its output - wherever that is.
Using exec() is OK when you need to call a program from the underlying operating system, but there are easier ways to do this if both the caller and the callee are Java programs.
Now, if your jar is gonna change dynamically and you need to update your program according to a new jar, there are mechanisms for reloading its contents, for instance take a look ath this other post.
The JavaDocs for the Process class specifically point out that if you don't capture the output stream of the Process and promptly read it that the process could halt. If this is the case, then you wouldn't see the process that you started run.
I think you have to capture the stream like this :
BufferedReader stdInput = new BufferedReader(new InputStreamReader(runManager.getInputStream()),8*1024);
BufferedReader stdError = new BufferedReader(new InputStreamReader(runManager.getErrorStream()));
// read the output from the command
String s = null;
System.out.println("Here is the standard output of the command:\n");
while ((s = stdInput.readLine()) != null) {
System.out.println(s);
}
The exec function doesn't automatically lookup into the PATH to start a process, so you have to pass the complete path for the java binary.
You can do that by using the java.home system property, see this answer: ProcessBuilder - Start another process / JVM - HowTo?
No one here seemed to help me, so I went to ask my friend and I had it almost right. It abiously required the string to be an array.
solution:
String[] cmd = {"java", "-jar", currDir};
try {
Runtime.getRuntime().exec(cmd);
} catch (IOException e1) {
e1.printStackTrace();
}

Categories