running scripts through processbuilder - java

I'm trying to run Python, Ruby, C, C++, and Java scripts from a java program, and Processbuilder was suggested to me as a good way to run the scripts. From what I understand, Processbuilder mostly runs native files (.exe on windows, etc.). However, I have heard a few things about running scripts (nonnative) files using Processbuilder. Unfortunately, everything I find on the subject is incredibly vague.
If someone could clarify a way to run nonnative scripts such as Python, Ruby, etc. I would be most grateful!

You can check the ProcessBuilder documentation over at Sunoracle, but basically, you can run the interpreter for the scripting language and pass the script you want to run to it.
For example, let's say you have a script in /home/myuser/py_script.py, and python is in /usr/bin/
class ProcessRunner
{
public static void main(String [] args)
{
ProcessBuilder pb = new ProcessBuilder("/usr/bin/python", "/home/myuser/py_script.py");
Process p = pb.start();
}
}
An extremely basic example, you can get fancier with changing the working directory and change the environment.
You can also construct ProcessBuilder with a String array or a subtype of List<String>. The first item in the list should be the program/executable you want to run, and all the following items are arguments to the program.
String pbCommand[] = { "/usr/bin/python", "/home/myuser/py_script.py" };
ProcessBuilder pb = new ProcessBuilder(pbCommand);
Process p = pb.start();

To avoid having to manually enter the entire location of the script, which may also result in portability issues, here's what I did:
String pwd = System.getProperty("user.dir");
ProcessBuilder pb = new ProcessBuilder("/usr/bin/python", pwd+'/'+scriptName, arg1, arg2);
Process p = pb.start();

Related

Java Run System Command and Ignore Output

Is there any sane way to run a system command from Java that ignores STDOUT and STDERR? For example, when I try the following:
Process p = Runtime.getRuntime().exec("some_executable_path param1 param2 >NUL 2>&1");
Java tries to parse the command, and ends up escaping the arguments (e.g., prevents the output from being redirected). If I don't redirect STDOUT/STDERR, the buffers fill up and prevent the system call from exiting. The following does what I want, but is extremely cumbersome and creates expensive resources just to throw the output of the system call away:
ProcessBuilder pb = new ProcessBuilder("some_executable_path", "param1", "param2");
pb.redirectErrorStream(true);
final Process p = pb.start();
final Thread redirectToNull = new Thread(() -> {
final InputStream stdout = process.getInputStream();
try {
while (stdout.read() != -1);
} catch (final Exception e) {
// Don't care
}
}, "Output Consumer Thread");
redirectToNull.setDaemon(true);
redirectToNull.start();
I realize the Java design team is known to be masochistic, but this is ridiculous. I would prefer to deliver a batch or Perl script that wraps the system call with my application rather than use the above code. There has to be an easier way to accomplish this.
So the question is, is there any sane way to run a system command from within Java and ignore the output printed to STDOUT/STDERR?
It's not that Java 'prevents' redirection, it just doesn't affirmatively do it, and neither does your program. When you give CMD a command like program arg1 arg2 >out 2>err <in, it is CMD that sets up those redirections and then invokes program with arg1 arg2 only, not >out etc. On Unix the shells do the same -- there is a choice of several shells, but all of them handle redirection like this. Similarly pipes are set up by CMD or shells, not by either or all of the programs run in those pipes.
Thus on Windows the way to do this is either run CMD and have it do the redirections:
Process p = new ProcessBuilder ("cmd", "/c", "program arg1 arg2 >NUL 2>&1").start();
// this uses CMD's default parsing for args, so they must not contain space
// unless you insert 'data' quotes, or things that look like a substitutable %var%
or (assuming Java7+) tell ProcessBuilder to do the redirections:
pb.redirectOutput (new File("NUL")).redirectErrorStream(true)

python's print causing errors in ProcessBuilder Java

I'm trying to use ProcessBuilder in Java to run some python code. print is causing errors but print() works fine.
The java code is:
List<String> list = Arrays.asList("python", "C:/.../test.py");
ProcessBuilder pb = new ProcessBuilder(list);
Process process = pb.start();
It generates a file if test.py is this
open('C:/.../outputtest2.txt', 'a')
print(5)
but it doesnt't work if test.py is this
open('C:/.../outputtest2.txt', 'a')
print 5
Both work fine if I run the script from the command line. I've tried changing and even removing Python 3 from the PATH but still getting the error. I can't find any similar errors online.
This is a python syntax error. As you know in python 3 you have to put parentheses for print function. for some reasons ( e.g. system cache) your ProcessBuilder use python 3 for running your python code.
This problem should solve by restarting the computer but if it doesn't work, you can run it manually with python 2.x
For example :
List<String> list = Arrays.asList("C:/python27/python.exe", "C:/.../test.py");
ProcessBuilder pb = new ProcessBuilder(list);

Running a bash command from within a Java program

Consider a piece of Java code:
import java.io.IOException;
public class Demo{
public static void main(String []args) throws IOException{
...
String abc="i am here";
System.out.println(abc);
}
}
I want to run - echo "THIS IS STUFF FOR THE FILE" >> file1.txt - immediately after the System.out.println() line, assuming file1.txt is in the same directory.
The ProcessBuilder class is the more modern version.
import static java.lang.ProcessBuilder.Redirect.appendTo;
ProcessBuilder pb = new ProcessBuilder("/bin/echo", "THIS IS STUFF FOR THE FILE");
pb.redirectOutput(appendTo(new File("file1.txt")));
Process p = pb.start();
Notice that this calls /bin/echo directly instead of having bash look through the PATH. That's safer, as there is no chance of getting a hacked echo. Also, since this doesn't use bash, Java is used to redirect the output.
Use the Runtime.getRuntime().exec() command like so:
Runtime.getRuntime().exec("echo 'THIS IS STUFF FOR THE FILE!' > file1.txt");
The documentation can be read here.
If it's not working or you find the command handy and want to know more about it, do yourself a favour and read this. It will save you sweat.

Setting nice value of Java program running on linux

I want my Java program to lower it's priority some so it doesn't overwhelm the system. My initial thought was to use Thread.currentThread().setPriority(5) but that appears to be merely its priority within the JVM.
Then I thought maybe I'd cludge it and invoke a system command, but Thread.getId() is also merely the JVM's id, so I don't even know what process id to pass to renice.
Is there any way for a Java program to do something like this?
Since we must do it in a platform dependent way, I run a shell process from java and it renices its parent. The parrent happens to be our java process.
import java.io.*;
public class Pid
{
public static void main(String sArgs[])
throws java.io.IOException, InterruptedException
{
Process p = Runtime.getRuntime().exec(
new String[] {
"sh",
"-c",
"renice 8 `ps h -o ppid $$`"
// or: "renice 8 `cat /proc/$$/stat|awk '{print $4}'`"
}
);
// we're done here, the remaining code is for debugging purposes only
p.waitFor();
BufferedReader bre = new BufferedReader(new InputStreamReader(
p.getErrorStream()));
System.out.println(bre.readLine());
BufferedReader bro = new BufferedReader(new InputStreamReader(
p.getInputStream()));
System.out.println(bro.readLine());
Thread.sleep(10000);
}
}
BTW: are you Brad Mace from jEdit? Nice to meet you.
If your program is the only running java program, then you can run
renice +5 `pgrep java`
In addition to renice - you may also use ionice comand. For example :
ionice -c 3 -n 7 -p PID
Also look at https://github.com/jnr/jnr-posix/.
This POSIX library should allow you to get at some of the Linux Posix Nice functions like...
https://github.com/jnr/jnr-posix/blob/master/src/main/java/jnr/posix/LibC.java for the OS level setPriority(), i.e. setpriority(2)
jnr-posix is also in Maven.
Use:
nice --adjustment=5 java whatever
to run your java program and assign the priority in just one step.
My suggestion is to invoke your java application from a bash script or start/stop service script then find the process id after startup and renice it.

Running a program from within Java code

What is the simplest way to call a program from with a piece of Java code? (The program I want to run is aiSee and it can be run from command line or from Windows GUI; and I am on Vista but the code will also be run on Linux systems).
Take a look at Process and Runtime classes. Keep in mind that what you are trying to accomplish is probably not platform independent.
Here is a little piece of code that might be helpful:
public class YourClass
{
public static void main(String args[])
throws Exception
{
Runtime rt = Runtime.getRuntime();
Process proc = rt.exec("name_of_your_application.exe");
int exitVal = proc.exitValue();
System.out.println("Process exitValue: " + exitVal);
}
}
One question in S.O. discussing similiar issues. Another one. And another one.
You can get a runtime instance using Runtime.getRuntime() and call the runtime's exec method, with the command to execute the program as an argument.
For example:
Runtime runTime = Runtime.getRuntime ();
Process proc = rt.exec("iSee.exe");
You can also capture the output of the program by using getting the InputStream from the process.
The difficulty you will run into is how to get the application to know the path. You may want to use an xml or config file, but if you use this link, it should explain how to run a file:
http://www.javacoffeebreak.com/faq/faq0030.html
You may also want to consider passing in some kind of argument to your program to facilitate finding the specific program you want to run.
This could be with command line arguments, properties files or system properties.

Categories