How can execute command R CMD Rserve from Java

212 views Asked by At

Im trying to start Rserve from my java backend using Runtime exec function. I can normally execute linux command like killall to stop the process but i was unable to start it. You can start Rserve from command line using this

R CMD Rserve

and works ok. But from java:

Process pr = Runtime.getRuntime().exec("R CMD Rserve")

its not working. How can i use this command from java then?

3

There are 3 answers

0
borja On BEST ANSWER

i get it work changing the way i was trying to invoke the process. The rzwitserloot's answer was really useful to figure this. I dont know about the internals of "R CMD" command but it dont like to java process builder. There is other way to invoke Rserver daemon within R console so i tried to do it this way. This works fine:

List<String> args = List.of("/bin/sh", "-c","echo 'library(Rserve);Rserve(args=\"--no-save --slave\");'| /usr/lib/R/bin/R --no-save --slave");
        ProcessBuilder pb = new ProcessBuilder(args);
        Process p = pb.start();
1
Tomino On

I have looked at some documentation and found something. Try this:

try {
    String command ="R CMD Rserve";
    BufferedWriter out = new BufferedWriter(new FileWriter(new File("path/to/file"), true));
    final Process process = Runtime.getRuntime().exec(command);
    BufferedReader buf = new BufferedReader(new InputStreamReader(process.getInputStream()));
    String line;
    while ((line = buf.readLine()) != null) {
        out.write(line);
        out.newLine();
    }
    buf.close();
    out.close();
    int returnCode = process.waitFor();
    System.out.println("Return code = " + returnCode);
    } catch (Exception e) {
        e.printStackTrace();
    }

Let me know if that helped :)

0
rzwitserloot On

exec is not the same thing as 'it works when I type it in a shell'. After all, when you type it 'on the command line', that 'command line' is, itself, an application. Usually it is /bin/bash, but there are many, many shells.

A shell doesn't just take what you type and toss it verbatim at the OS and goes 'here. Run this'. It takes the stuff you typed and massages it. Applies transformations to it. then, the massaged stuff is tossed at the OS with 'here, run this'.

Java is similar, in that it takes what you wrote, massages it, and then tosses it at the OS.

These massagings are quite different.

For example, /bin/bash will turn *.txt into the result of running ls *.txt. Java doesn't do this, so if you try to run e.g. exec("/bin/ls *.txt") in java, it doesn't work. ls tries to find a file literally called *.txt, and reports it can't find it.

The trick is to be aware of all the many many things bash does to your input, and rely on none of this. What java actually does for massaging is OS dependent, so don't depend on that either. Thus, some rules:

  1. Always use absolute paths. Don't run R CMD Rserve. Run /usr/local/share/R/bin/R CMD RServe, for example. I have no idea where R is, but it's an executable somewhere on your system. Find the absolute path; one of the massagings bash does is apply $PATH. Java may or may not apply path, and who knows what your path setting even is, and if the java process has the same PATH setting as your bash; it probably doesn't, so, definitely don't rely on that and always, always, write an absolute path.

  2. Be aware of bash internals and never use them. ls and ps are often internals; by definition then java can't run them. If you must rely on bashisms, you have to actually run bash. It's not exec("ls"), it's exec("/bin/bash", "-c", "ls").

  3. Never rely on java's ability to split on spaces. Do not ever use exec(cmd), and do not ever use Runtime.getRuntime().exec. Consider these banned methods. Make a ProcessBuilder, and always the the List<String> variant (where the first entry in the list is the absolute path to a command and the rest are command line args.

This gets you to:

// to stop

List<String> args = List.of("/bin/bash", "-c", "killall -9 RServe");
ProcessBuilder pb = new ProcessBuilder(args);
// if you need to read what bash produces, config that here.
pb.start();

// to start
List<String> args = List.of("/abs/path/to/R", "CMD", "RServe");
ProcessBuilder pb = new ProcessBuilder(args);
// if you need to read what bash produces, config that here.
pb.start();

Of course, you won't be able to kill -9 unless your java process is the one that started it, so most likely it is impossible to do this from java - you just don't have the rights.