I am trying to invoke some r code from within Java,pretty much like this:
private void makeMatrix() throws ScriptException {
try {
Runtime.getRuntime().exec(" Rscript firstscript.r");
System.out.println("Script executed");
} catch (IOException ex) {
System.out.println("Exception");
System.out.println(ex.getMessage());
}
}
Well, I get the "Script executed" print.
My (well, not really mine, just to test) r-Code is fairly simple, pretty much just to see it works at all:
x=seq(0,2,by=0.01)
y=2*sin(2*pi*(x-1/4))
plot(x,y)
So, it should not do much more than plot a sinus.
However, should not there be some kind of popup where you can actually see the plot?
because there is none. What am I doing wrong?
Edit: In response to the comments I got here I edited the r-file, adding:
jpeg('rplot.jpg')
plot(x,y)
dev.off()
to it.
However, If I then try to find rplot.jpg on my system it simply is not there.
You passed a relative directory to the jpeg function . This makes it relative to R's current working directory (the value returned by getwd).
Try printing this value to see where that is (on Windows, by default it's in "My Documents" for the current user)
print(getwd())
or passing an absolute path to jpeg.
jpeg('c:/rplot.jpg')
plot(x,y)
dev.off()
To get an absolute path, use pathological::standardize_path or R.utils::getAbsolutePath.
You can wait for the Process (exec returns a Process object) to finish
with waitFor, and check the exit value: it should be 0.
If it is not zero, you probably need to specify the path of the script.
public static void main( String[] args ) throws IOException, InterruptedException {
Process p = Runtime.getRuntime().exec("Rscript /tmp/test.R");
System.out.println("Started");
p.waitFor();
if( p.exitValue() != 0 )
System.out.println("Something went wrong");
else
System.out.println("Finished");
}
If the exit value is not 0, you can look at the stdout and stderr of the process,
as suggested in Andrew's comment.
public static void main(String[] args) throws IOException, InterruptedException {
System.out.println("test...");
Process p = Runtime.getRuntime().exec(new String[] {
"Rscript",
"-e",
"print(rnorm(5)))" // Intentional error, to produce an error message
} );
System.out.println("Started");
String line = null;
System.out.println("Stdout:");
BufferedReader stdout = new BufferedReader( new InputStreamReader( p.getInputStream() ) );
while ( (line = stdout.readLine()) != null)
System.out.println(line);
System.out.println("Stderr:");
BufferedReader stderr = new BufferedReader( new InputStreamReader( p.getErrorStream() ) );
while ( (line = stderr.readLine()) != null)
System.out.println(line);
p.waitFor();
if( p.exitValue() != 0 )
System.out.println("Something went wrong, exit value=" + p.exitValue());
else
System.out.println("Finished");
}
As mentionned in the comments,
you need to explicitly open the device.
Since it is closed when the script terminates, you also need to add a delay.
x11() # Open a device (also works on Windows)
plot( rnorm(10) )
Sys.sleep(10) # Wait 10 seconds
Related
without using a redirect to file (">", ">>")
Process p = Runtime.getRuntime().exec("executable.exec");
BufferedReader input = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line;
while ((line = input.readLine()) != null) {
System.out.println(line);
}
Note that you should consume stdout and stderr concurrently, to prevent blocking. See this answer for more details.
Note that you may be able to get away with just taking stdout and not stderr. However if you .exe generates an error in some scenario, then your parent process can then block on the extra (unexpected) stream data. So it's always best to run your stream gathering concurrently.
If you use the Commandline type from plexus-utils, you can avoid a lot of the heavy lifting associated with commandline interaction, for example waiting for the process, escaping arguments etc. You can set the command to timeout if needed as well.
You can pass StreamConsumers to capture the stdout and stderr, the Commandline handling will pass the output to the consumers one line at a time.
Commandline cl = new Commandline();
cl.setExecutable( "dir" );
cl.setWorkingDirectory( workingDirectory.getAbsolutePath() );
cl.createArg().setValue( "/S" );
StreamConsumer consumer = new StreamConsumer() {
public void consumeLine( String line ) {
//do something with the line
}
};
StreamConsumer stderr = new StreamConsumer() {
public void consumeLine( String line ) {
//do something with the line
}
};
int exitCode;
try {
exitCode = CommandLineUtils.execute( cl, consumer, stderr, getLogger() );
} catch ( CommandLineException ex ) {
//handle exception
}
I'm not sure why, but while I am trying to debug, I find this is very weird:
As you see in the image, the value of in.readLine() is null and in.readLine() == null is true. But why it skips the if (in.readLine() == null) { ... line? But when I tried to place the breakpoint in line 266 and 267, it's entering the code on that condition.
The code:
private void startSOfficeService() throws InterruptedException, IOException {
if (System.getProperty("os.name").matches(("(?i).*Windows.*"))) {
try {
//Check if the soffice process is running
Process process = Runtime.getRuntime().exec("tasklist /FI \"IMAGENAME eq soffice.exe\"");
//Need to wait for this command to execute
int code = process.waitFor();
//If we get anything back from readLine, then we know the process is running
BufferedReader in = new BufferedReader(new InputStreamReader(process.getInputStream()));
if (in.readLine() == null) {
//Nothing back, then we should execute the process
String[] SOFFICE_CMD = { SOFFICE_SERVICE_PATH,
"-accept=socket,host=" + SOFFICE_SERVICE_HOST + ",port=" + SOFFICE_SERVICE_PORT + ";urp;",
"-invisible",
"-nologo"};
process = Runtime.getRuntime().exec(SOFFICE_CMD);
code = process.waitFor();
System.out.println("soffice script started");
} else {
System.out.println("soffice script is already running");
}
in.close();
in = null;
System.gc();
} catch(Exception e) {
e.printStackTrace();
}
}
}
When your debugger evaluates in.readLine(), it consumes from the reader. Therefore, if you were on the last line of whatever is being read, in.readLine() would be non-null, putting control in the else, but when you evaluate in.readLine() to display in the debugger, it reads again, finds that there are no more lines, and returns null as the value to show in the debugger.
To see the real picture, assign in.readLine() to a variable first, and watch the value of that variable, which won't change by simply reading it.
First, this is my code :
import java.io.*;
import java.util.Date;
import com.banctecmtl.ca.vlp.shared.exceptions.*;
public class PowershellTest implements Runnable {
public static final String PATH_TO_SCRIPT = "C:\\Scripts\\ScriptTest.ps1";
public static final String SERVER_IP = "XX.XX.XX.XXX";
public static final String MACHINE_TO_MOD = "MachineTest";
/**
* #param args
* #throws OperationException
*/
public static void main(String[] args) throws OperationException {
new PowershellTest().run();
}
public PowershellTest(){}
#Override
public synchronized void run() {
String input = "";
String error = "";
boolean isHanging = false;
try {
Runtime runtime = Runtime.getRuntime();
Process proc = runtime.exec("powershell -file " + PATH_TO_SCRIPT +" "+ SERVER_IP +" "+ MACHINE_TO_MOD);
proc.getOutputStream().close();
InputStream inputstream = proc.getInputStream();
InputStreamReader inputstreamreader = new InputStreamReader(inputstream);
BufferedReader bufferedreader = new BufferedReader(inputstreamreader);
proc.waitFor();
String line;
while (!isHanging && (line = bufferedreader.readLine()) != null) {
input += (line + "\n");
Date date = new Date();
while(!bufferedreader.ready()){
this.wait(1000);
//if its been more then 1 minute since a line has been read, its hanging.
if(new Date().getTime() - date.getTime() >= 60000){
isHanging = true;
break;
}
}
}
inputstream.close();
inputstream = proc.getErrorStream();
inputstreamreader = new InputStreamReader(inputstream);
bufferedreader = new BufferedReader(inputstreamreader);
isHanging = false;
while (!isHanging && (line = bufferedreader.readLine()) != null) {
error += (line + "\n");
Date date = new Date();
while(!bufferedreader.ready()){
this.wait(1000);
//if its been more then 1 minute since a line has been read, its hanging.
if(new Date().getTime() - date.getTime() >= 60000){
isHanging = true;
break;
}
}
}
inputstream.close();
proc.destroy();
} catch (IOException e) {
//throw new OperationException("File IO problem.", e);
} catch (InterruptedException e) {
//throw new OperationException("Script thread problem.",e);
}
System.out.println("Error : " + error + "\nInput : " + input);
}
}
I'm currently trying to run a powershell script that will start/stop a vm (VMWARE) on a remote server. The script work from command line and so does this code. The thing is, I hate how I have to use a thread (and make it wait for the script to respond, as explained further) for such a job. I had to do it because both BufferedReader.readline() and proc.waitFor() hang forever.
The script, when ran from cmd, is long to execute. it stall for 30 sec to 1 min from validating authentification with the server and executing the actual script. From what I saw from debugging, the readline hang when it start receiving those delays from the script.
I'm also pretty sure it's not a memory problem since I never had any OOM error in any debugging session.
Now I understand that Process.waitFor() requires me to flush the buffer from both the error stream and the regular stream to work and so that's mainly why I don't use it (I need the output to manage VM specific errors, certificates issues, etc.).
I would like to know if someone could explain to me why it hangs and if there is a way to just use the typical readline() without having it to hang so hard. Even if the script should have ended since a while, it still hang (I tried to run both the java application and a cmd command using the exact same thing I use in the java application at the same time, left it runingfor 1h, nothing worked). It is not just stuck in the while loop, the readline() is where the hanging is.
Also this is a test version, nowhere close to the final code, so please spare me the : this should be a constant, this is useless, etc. I will clean the code later. Also the IP is not XX.XX.XX.XXX in my code, obviously.
Either explanation or suggestion on how to fix would be greatly appreciated.
Ho btw here is the script I currently use :
Add-PSSnapin vmware.vimautomation.core
Connect-VIServer -server $args[0]
Start-VM -VM "MachineTest"
If you need more details I will try to give as much as I can.
Thanks in advance for your help!
EDIT : I also previously tested the code with a less demanding script, which job was to get the content of a file and print it. Since no waiting was needed to get the information, the readline() worked well. I'm thus fairly certain that the problem reside on the wait time coming from the script execution.
Also, forgive my errors, English is not my main language.
Thanks in advance for your help!
EDIT2 : Since I cannot answer to my own Question :
Here is my "final" code, after using threads :
import java.io.*;
public class PowershellTest implements Runnable {
public InputStream is;
public PowershellTest(InputStream newIs){
this.is = newIs;
}
#Override
public synchronized void run() {
String input = "";
String error = "";
try {
InputStreamReader inputstreamreader = new InputStreamReader(is);
BufferedReader bufferedreader = new BufferedReader(inputstreamreader);
String line;
while ((line = bufferedreader.readLine()) != null) {
input += (line + "\n");
}
is.close();
} catch (IOException e) {
//throw new OperationException("File IO problem.", e);
}
System.out.println("Error : " + error + "\nInput : " + input);
}
}
And the main simply create and start 2 thread (PowerShellTest instances), 1 with the errorStream and 1 with the inputStream.
I believe I made a dumb error when I first coded the app and fixed it somehow as I reworked the code over and over. It still take a good 5-6 mins to run, which is somehow similar if not longer than my previous code (which is logical since the errorStream and inputStream get their information sequentially in my case).
Anyway, thanks to all your answer and especially Miserable Variable for his hint on threading.
First, don't call waitFor() until after you've finished reading the streams. I would highly recommend you look at ProcessBuilder instead of simply using Runtime.exec, and split the command up yourself rather than relying on Java to do it for you:
ProcessBuilder pb = new ProcessBuilder("powershell", "-file", PATH_TO_SCRIPT,
SERVER_IP, MACHINE_TO_MOD);
pb.redirectErrorStream(true); // merge stdout and stderr
Process proc = pb.start();
redirectErrorStream merges the error output into the normal output, so you only have to read proc.getInputStream(). You should then be able to just read that stream until EOF, then call proc.waitFor().
You are currently waiting to complete reading from inputStream before starting to read from errorStream. If the process writes to its stderr before stdout maybe you are getting into a deadlock situation.
Try reading from both streams from concurrently running threads. While you are at it, also remove proc.getOutputStream().close();. It shouldn't affect the behavior, but it is not required either.
Related to this question
Java writing to a deleted file
only in my case i'm doing the reading. And per that comment, yes, Windows block deletes and Unix doesn't. and under unix never throws any IOException
the code is a poor-man's tail -f, where i have a java thread watching each log file in a directory. my current issue is if the file is deleted, i'm not handling it. I need to abort and start a new thread or something. I didn't even realize it was an issue, as the below code throws no exceptions under Unix
the code
BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(f)));
String line = null;
while (true) {
try {
line = br.readLine();
// will return null if no lines added
} catch (IOException e) {
e.printStackTrace();
}
if (line == null) {
// sleep if no new lines added to file
Thread.sleep(1000);
} else {
// line is not null, process line
}
}
Tomorrow I'll try adding this check before the sleep, maybe is sufficient
if (!f.exists()) {
// file gone, aborting this thread
return;
}
Anyone have other ideas?
When you reach the end of a file, BufferedReader should always return a null whether it has been deleted or not. Its not something you should have to check for.
Can you show us some code because its very hard to stop the BufferedReader not returning a null?
This program
public class Main {
public static void main(String... args) throws IOException {
PrintWriter pw = new PrintWriter("file.txt");
for (int i = 0; i < 1000; i++)
pw.println("Hello World");
pw.close();
BufferedReader br = new BufferedReader(new FileReader("file.txt"));
br.readLine();
if (!new File("file.txt").delete())
throw new AssertionError("Could not delete file.");
while (br.readLine() != null) ;
br.close();
System.out.println("The end of file was reached.");
}
}
On windows prints
AssertionError: Could not delete file.
On Linux prints
The end of file was reached.
You could watch your directory using WatchService API for changes and act accordingly
without using a redirect to file (">", ">>")
Process p = Runtime.getRuntime().exec("executable.exec");
BufferedReader input = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line;
while ((line = input.readLine()) != null) {
System.out.println(line);
}
Note that you should consume stdout and stderr concurrently, to prevent blocking. See this answer for more details.
Note that you may be able to get away with just taking stdout and not stderr. However if you .exe generates an error in some scenario, then your parent process can then block on the extra (unexpected) stream data. So it's always best to run your stream gathering concurrently.
If you use the Commandline type from plexus-utils, you can avoid a lot of the heavy lifting associated with commandline interaction, for example waiting for the process, escaping arguments etc. You can set the command to timeout if needed as well.
You can pass StreamConsumers to capture the stdout and stderr, the Commandline handling will pass the output to the consumers one line at a time.
Commandline cl = new Commandline();
cl.setExecutable( "dir" );
cl.setWorkingDirectory( workingDirectory.getAbsolutePath() );
cl.createArg().setValue( "/S" );
StreamConsumer consumer = new StreamConsumer() {
public void consumeLine( String line ) {
//do something with the line
}
};
StreamConsumer stderr = new StreamConsumer() {
public void consumeLine( String line ) {
//do something with the line
}
};
int exitCode;
try {
exitCode = CommandLineUtils.execute( cl, consumer, stderr, getLogger() );
} catch ( CommandLineException ex ) {
//handle exception
}