I'm building a front-end for my company's internal tool kit. Half the tools are written in python and then the other half are written in several other scripting languages. So I'm building a front-end in java using swing. So far I can invoke python scripts through the following code:
public class Foo
{
public static void main(String[] args)
{
try
{
Runtime r = Runtime.getRuntime();
Process p = r.exec("python foo.py");
BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream()));
p.waitFor();
String line = "";
while (br.ready())
System.out.println(br.readLine());
}
catch (Exception e)
{
String cause = e.getMessage();
if (cause.equals("python: not found"))
System.out.println("No python interpreter found.");
}
}
}
Which works beautifully but if the python script encounters any errors it doesn't print them out. How can I ensure that it also prints out any errors that it has?
The simple answer is to also read Process.getErrorStream.
The more complicated answer is that what you call Python likely refers to CPython which is just one implementation of the language. There is another implementation, Jython, which basically compiles Python into Java bytecode to be run on a JVM. This would allow tighter integration than simply invoking CPython via Java's Runtime.exec
P.S. Runtime.exec is sort of the old way of doing things. ProcessBuilder is often a much cleaner and more intuitive way of starting a sub-process in Java.
The prevois answer is Ok,here is a suggestion that you shoud release any resources of Process,like:
Process process = null;
try{
//xxxx
}catch(xxx){
}finally{
if(process!=null){
process.destroy();
}
}
The reason is that if you forgot to destroy process,the file handler involved would leak,
you will got an IOException show too many open files finally.
Related
I have created a java application in eclipse. The application used Rserve package to connect to R and run r scripts. Before running my application, i have to start rserve from within Rstudio like this:
library(Rserve)
Rserve()
This Java code would be bundled as an executable file, so is there a way that Rserve() is invoked automatically(in windows) as soon as the code is run so that I can skip this manual step of starting Rserve using through RStudio?
The https://github.com/yannrichet/rsession project achieves exactly that for you.
Though it might be interesting to have a look at this: https://github.com/subes/invesdwin-context-r
As it integrates RSession and keeps a pool of Rserve connections for performance reasons without you having to do much for it. You can also switch to other runtime solutions like JRI, RCaller, Renjin without having to change your script code.
I'm not sure if there's a cleaner way to do this, but the way I've solved this is by starting it up console style from within my java program. For this to work, you have to put the path to the R executables in your systems path:
public Process rserve = null;
public static void startRServer() throws InterruptedException, IOException {
// check the runtime environment to see if there's an active Rserve running
String existingRserve = "";
try {
Process p = Runtime.getRuntime().exec("tasklist /FI \"IMAGENAME eq Rserve.exe\"");
p.waitFor();
BufferedReader in = new BufferedReader(new InputStreamReader(p.getInputStream()));
existingRserve = in.readLine();
} catch(IOException e){}
if(rserve == null || existingRserve.contains("No tasks are running")) {
// start and rserve if we don't have one for this run yet,
// or if it has unexpectedly failed since we last used it
try {
rserve = Runtime.getRuntime().exec("Rscript -e \"library(Rserve); Rserve()\"");
rserve.waitFor();
} catch (IOException e) {
System.out.print("*** R Error: Unable to start the R server ***");
}
}
}
This is essentially what I'm doing:
ProcessBuilder pb = new ProcessBuilder("./myProgram","myArguments");
pb.redirectInput(new File("myFile.txt"));
try
{
Process p = pb.start();
}
catch (Exception e)
{
...
}
I'm using JDK 8 at home and this code snippet works just fine. However, this is part of a school assignment and unfortunately the school is running JDK 6, so methods like redirectInput() don't exist in the ProcessBuilder class.
Is there any way I'd be able to redirect input the same way that redirectInput() does? I've been searching various solutions that people with similar problems were offered but have yet to find a working one.
Thanks!
How do I run an external command (via a shell) from a Java program, such that no redirection takes place, and wait for the command to end? I want the file descriptors of the external program to be the same as those of the Java program. In particular I do not want the output to be redirected to a pipe that the Java program is reading. Having the Java program relay the output is not a solution.
This means that a plain invocation of java.lang.Runtime.exec is not the solution. I presume that java.lang.ProcessBuilder is involved, but how do I specify that output and error streams must be the same as the calling Java process?
class A {
public static void main(String[] args) {
try {
ProcessBuilder pb = new ProcessBuilder("echo", "foo");
/*TODO: pb.out = System.out; pb.err = System.err;*/
Process p = pb.start();
p.waitFor();
} catch (Exception e) {
System.err.println(e);
System.exit(1);
}
}
}
(This may or may not be the right approach.)
In other words, I'm looking for Java's system, but all I can find is (roughly) popen.
Here's an example of a situation where relaying cannot work: if the subprocess writes to both stdout and stderr and the Java program is relaying, then the Java program has no way to know the order of the write calls in the subprocess. So the order of the output on stdout and stderr from the Java program will be observably different if the two streams end up in the same file. Mixing stdout and stderr is of course not a solution because the caller may want to keep them separate.
While I think this question is of general interest, a Linux-specific solution would solve my immediate problem.
This is the intent of ProcessBuilder.redirectError/redirectOutput which were introduced in Java 7. Using Redirect.INHERIT will make the child process share stderr/stdout with the Java process:
class A {
public static void main(String[] args) {
try {
ProcessBuilder builder = new ProcessBuilder("echo", "foo");
builder.redirectError(ProcessBuilder.Redirect.INHERIT);
builder.redirectOutput(ProcessBuilder.Redirect.INHERIT);
Process p = builder.start();
p.waitFor();
} catch (Exception e) {
System.err.println(e);
System.exit(1);
}
}
}
You might take a look at the NuProcess project. Disclaimer: I wrote it. It provides non-blocking I/O from spawned processes. You still have to relay in Java (you receive callbacks), but because it is using epoll() in the case of Linux, I would expect it to preserve the order of the underlying program. Only a single thread is epoll()'ing the pipes so you won't get any thread scheduling order issues.
I'm 100% order would be preserved on MacOS X, or any BSD variant, because it uses a kqueue which is definitely ordered. Anyway, you might want to give it a shot, it's trivial to code and test.
You can't. By default all standard I/O of the child process are redirected to the parent process (the jvm running your java program).
from the javadoc of the Process class:
By default, the created subprocess does not have its own terminal or
console. All its standard I/O (i.e. stdin, stdout, stderr)
operations will be redirected to the parent process, where they can be
accessed via the streams obtained using the methods getOutputStream(),
getInputStream(), and getErrorStream(). The parent process uses these
streams to feed input to and get output from the subprocess. 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, or even deadlock.
This is my first question on stackoverflow so I'll try to keep it concise and relevant.
I'm currently creating a Java program that is attempting to call an external program located on the system, in order to do this however I am required to call a shell script that sets up links to the relevant libraries to ensure that the system is linked to these before the external program can be executed.
The issue at hand is that I cannot invoke the shell script through Java, I've researched high and low and am aware that of alternative ways such as the use of the ProcessBuilder class. Unfortunately I'm quite new to the world of trying to invoke command line statements through Java and so I'm stuck for answers.
An example of the code I am using can be found below:
private void analyse_JButtonActionPerformed(java.awt.event.ActionEvent evt) {
// Get project path for copying of Fortran program to folder and execution
String projectPath = Newproject_GUI.getProjectPath();
String sourcePath [] = {"/bin/sh ", "-c ","source ~/set_env_WRF_gnu.sh"} ;
Runtime fortranAnalyser = Runtime.getRuntime();
try {
Process p = fortranAnalyser.exec("cp main.exe " + projectPath);
Process k = fortranAnalyser.exec(sourcePath);
BufferedReader is = new BufferedReader(new InputStreamReader(k.getInputStream()));
String line;
while ((line = is.readLine()) != null) {
System.out.println(line);
}
} catch (IOException ex) {
Logger.getLogger(Analyser_GUI.class.getName()).log(Level.SEVERE, null, ex);
}
}
Process p works fine and does indeed copy main.exe to the intended directory when the method is called. Process k however does not and this is where the issue is.
Thanks in advance.
The issue is "source" is internal command of BASH (you are using "sh" but that is just BASH in the simplified mode). So what you do is:
you spawn new process "sh" and source something there (setting some VARIABLES I guess)
the process ends and all VARIABLES are lost
you spawn another process, but VARIABLES are already gone
I am not sure if you use those variables later on, but according to the script name it is probably setting some. Don't do that like this.
By the way if you only want to execute script in bash, you don't need to source it. To get it's side effects, just execute it with:
String sourcePath [] = {"/bin/sh ", "/home/XYZ/set_env_WRF_gnu.sh"} ;
Please note you cannot use ~ in this case, use Java to get your home dir.
I have a CMS server that provides a client library. I'd like to be able to drive the CMS interactively from the command line.
The basic approach would be:
Create a connection to the CMS
Add the CMS connection object to the REPL context
Connect the REPL to stdout/stderr/stdin
Kick off a daemon thread for to keep the REPL running.
I was hoping that I could perhaps leverage Groovy to do this but haven't managed to get it working.
Is there a library that provides REPL support?
Can you provide a simple example?
If you don't mind using Scala as your language, you can use the Scala REPL to explore java libraries. You can do this in a number of ways, either with
$ scala -classpath yourjarfileshere.jar
or if you're using maven:
mvn scala:console
If all you're doing is playing (not scripting or anything), then this is a possible way to go.
If you wish to embed your repl, and you're still willing to use Scala, you can look at the answer to these questions: Drop into interpreter during arbitrary scala code location
and Launch Scala REPL programatically?
Groovy also has a repl, groovysh, which you can use to explore.
I got this working with Groovy.
Example
public static void main(final String[] args) {
Binding binding = new Binding();
// Configure your bindings here.
Groovysh shell = new Groovysh(binding, new IO());
shell.run(args);
}
Known Issues
However, it won't work when the app is started from Eclipse (ie using the Eclipse 'console' view). To work around this you must update the Eclipse launch configuration to pass the following VM argument:
-Djline.terminal=jline.UnsupportedTerminal.
More information
Documentation of the Groovy Shell.
Wikipedia page for REPL mentions BeanShell. Would that work?
Beanshell can be run as repl in your own thread/main within your application:
public static void main(String[] args) throws Exception{
Reader inreader = new InputStreamReader(System.in);
Interpreter i = new Interpreter(inreader, System.out, System.err, true);
try {
BufferedReader in = new BufferedReader(inreader);
String str;
while ((str = in.readLine()) != null) {
i.eval(str);
}
in.close();
} catch (Exception e) {
}
}
that example runs in eclipse fine, you type at it in the console window of eclipse then it will talk back to you fine.