A simple example of using phantomJs in Java will block undefinitely:
public void runPhantomJs(String path, String command) {
Process process;
String outFile = "a11.txt";
try {
process = Runtime.getRuntime().exec(path+ " " + command + " > " +outFile);
int exitStatus = process.waitFor();
//String status = (exitStatus == 0 ? "SUCCESS:" : "ERROR:");
File f = new File(outFile);
if (f.exists()) {
BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(f),"UTF-8"));
String str;
while ((str = in.readLine()) != null) {
System.out.println(str);
}
in.close();
System.out.println(str);
}
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
The script execute is very simple, but it returns a whole page on the console:
var webPage = require('webpage');
var page = webPage.create();
page.open('http://www.google.com/', function(status) {
if (status !== 'success') {
console.log('1');
phantom.exit();
} else {
console.log(page.content);
phantom.exit();
}
});
Note that on the pasted code I've added a "> a11.txt" to see if it worked better to read a file instead of reading the output directly. It should be faster, but for some reason it doesn't work. I suppose the redirection > doesn't work.
So I got my code to work. Apparently the output of phantomjs has to be read or the buffer will fill up completely, blocking further execution.
So I think your code will work if you modify it like so:
process = Runtime.getRuntime().exec(path+ " " + command + " > " +outFile);
BufferedInputStream bis = new BufferedInputStream(process.getInputStream());
bis.close();
process.waitFor();
...
If it doesn't work, try using ProcessBuilder. This is my working code:
try {
String phantomJsExe = configuration.getPhantomJsExe().toString();
String phantomJsScript = configuration.getPhantomJsScript().toString();
String urlsTextFile = configuration.getPhantomJsUrlsTextFile().toString();
Process process = new ProcessBuilder(phantomJsExe, phantomJsScript, urlsTextFile).start();
BufferedInputStream bis = new BufferedInputStream(process.getInputStream());
bis.close();
process.waitFor();
} catch (Exception ex) {
ex.printStackTrace();
}
Related
I am executing a command which returns me the Revision number of a file; 'fileName'. But if there is some problem executing the command, then the application hangs up. What can I do to avoid that condition? Please find below my code.
String cmd= "cmd /C si viewhistory --fields=revision --project="+fileName;
Process p = Runtime.getRuntime().exec(cmd) ;
BufferedReader in = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line = null;
while ((line = in.readLine()) != null) {
System.out.println(line);
}
} catch (Exception e) {
e.printStackTrace();
}
I guess the issue is that you are only reading InputStream and not reading ErrorStream. You also have to take care that both the streams are read in parallel. It may so happen that currently the data piped from the output stream fills up the OS buffer, your exec command will be automatically be suspended to give your reader a chance to empty the buffer. But the program will still be waiting for the output to process. Hence, the hang occurs.
You can create a separate class to handle both the Input and Error Stream as follows,
public class ReadStream implements Runnable {
String name;
InputStream is;
Thread thread;
public ReadStream(String name, InputStream is) {
this.name = name;
this.is = is;
}
public void start () {
thread = new Thread (this);
thread.start ();
}
public void run () {
try {
InputStreamReader isr = new InputStreamReader (is);
BufferedReader br = new BufferedReader (isr);
while (true) {
String s = br.readLine ();
if (s == null) break;
System.out.println ("[" + name + "] " + s);
}
is.close ();
} catch (Exception ex) {
System.out.println ("Problem reading stream " + name + "... :" + ex);
ex.printStackTrace ();
}
}
}
The way you use it is as follows,
String cmd= "cmd /C si viewhistory --fields=revision --project="+fileName;
Process p = Runtime.getRuntime().exec(cmd) ;
s1 = new ReadStream("stdin", p.getInputStream ());
s2 = new ReadStream("stderr", p.getErrorStream ());
s1.start ();
s2.start ();
p.waitFor();
} catch (Exception e) {
e.printStackTrace();
} finally {
if(p != null)
p.destroy();
}
This code is based on the same idea Arham's answer, but is implemented using a java 8 parallel stream, which makes it a little more concise.
public static String getOutputFromProgram(String program) throws IOException {
Process proc = Runtime.getRuntime().exec(program);
return Stream.of(proc.getErrorStream(), proc.getInputStream()).parallel().map((InputStream isForOutput) -> {
StringBuilder output = new StringBuilder();
try (BufferedReader br = new BufferedReader(new InputStreamReader(isForOutput))) {
String line;
while ((line = br.readLine()) != null) {
output.append(line);
output.append("\n");
}
} catch (IOException e) {
throw new RuntimeException(e);
}
return output;
}).collect(Collectors.joining());
}
You can call the method like this
getOutputFromProgram("cmd /C si viewhistory --fields=revision --project="+fileName);
Note that this method will hang if the program you are calling hangs, which will happen if it requires input.
I am having trouble finding the source of lag in my code. I believe I have narrowed the possible source down to this method.
Essentially I start a script, set it in a Process variable p, and grab the output from the script using a BufferedReader, and put it into an ArrayList.
Somehow I am getting lag when the script outputs (it outputs at a 5 minute interval)
Any ideas?
public void runCommand(String path)
{
if (SystemUtils.IS_OS_WINDOWS)
{
ProcessBuilder builder = new ProcessBuilder("cmd.exe", "/c", "cd " + path + " && " + this.getCommand());
builder.redirectErrorStream(true);
try
{
p = builder.start();
}
catch (IOException e)
{
e.printStackTrace();
}
}
else
{
try
{
String name = ManagementFactory.getRuntimeMXBean().getName();
String pid = name.substring(0, name.indexOf("#"));
p = Runtime.getRuntime().exec("./btrace.sh " + pid + " " + path + " " + this.getConfig().getPort());
}
catch (Exception e)
{
e.printStackTrace();
}
}
BufferedReader r = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line;
try
{
// Print out everything that's happening.
while (true)
{
line = r.readLine();
if (line == null)
{
break;
}
if (this.isDebugEnabled)
{
System.out.println("[Script Output]: " + line);
}
lines.add(line);
}
r.close();
}
catch (IOException e)
{
e.printStackTrace();
}
}
Seems like I found the cause of lag.
After researching how ArrayLists resize, I realized it could be performance taxing and tried using a Linked list.
So far seems like the issue is fixed.
Thanks!
I'm trying to restore a backed up .sql file using Java program. I'm posting the method below. But when I execute this the program halts for a long time. Then I executed same mysql command in command line(Windows) it's works charmingly.
Puzzled where I missed. What do you think ?
File file;
final JFileChooser fc = new JFileChooser();
int returnVal = fc.showOpenDialog(this);
if (returnVal == JFileChooser.APPROVE_OPTION) {
file = fc.getSelectedFile();
try {
System.out.println(file.getCanonicalPath());
String executeCmd = "mysql -u " + username + " -p" + password +" " + dbName+" < "+" \" "+file.getCanonicalPath()+"\" " ;
Process runtimeProcess;
runtimeProcess = Runtime.getRuntime().exec(executeCmd);
int processComplete = runtimeProcess.waitFor();
if (processComplete == 0) {
JOptionPane.showMessageDialog(Interface.mainFrame, "Database Backup restored successfully.", "Netmetering", 1);
} else {
System.out.println("Could not restore the backup");
}
} catch (IOException | InterruptedException ex) {}
...
String executeCmd = "mysql -u " + username + " -p" + password +" " + dbName+" < "+" \" "+file.getCanonicalPath()+"\" " ;
Process runtimeProcess = Runtime.getRuntime().exec(executeCmd);
InputStream is = runtimeProcess.getInputStream();
// Do one OR the other, but not both ;)
// If you don't care about the output, but I think it's a bit of waste personally...
while (is.read() != -1) {}
// I'd at least dump the output to the console...
int byteRead = -1;
while ((byteRead = is.read()) != -1) {
System.out.print((char)byteRead );
}
int processComplete = runtimeProcess.waitFor();
if (processComplete == 0) {...}
I would also recommend using ProcessBuilder over creating the Process manually like this, it handles the parameters better - IMHO
In general the correct way to run an external program is:
build you external program command line
build the ProcessBuilder and Process
build your own StreamRender
execute your external program
check the STDOUT and STDERR of your external program
verify the exit status of your external program
you can achieve this sequence as described following:
String command = "mysql -u... -p... dbname < file.sql";
ProcessBuilder pb = new ProcessBuilder(command);
Process pr;
try {
pr = pb.start();
StreamRender outputStdout = new StreamRender(pr.getInputStream(), StreamRender.STDOUT);
// your STDOUT output here
outputStdout.start();
StreamRender outputStderr = new StreamRender(pr.getErrorStream(), StreamRender.STDERR);
// your STDERR outpu here
outputStderr.start();
pr.waitFor();
if (pr.exitValue() != 0) {
// your external program fails
} else {
// ok, your external program was executed correctly
}
} catch (Exception e) {
// ...
} finally {
// ...
}
StreamRender.java
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
public class StreamRender extends Thread {
public static final String STDOUT = "STDOUT";
public static final String STDERR = "STDERR";
private InputStream inputStream;
private String inputType;
public StreamRender(InputStream inputStream, String inputType) {
this.inputStream = inputStream;
this.inputType = inputType;
}
public void run() {
try {
InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
BufferedReader br = new BufferedReader(inputStreamReader);
String line = null;
while ((line = br.readLine()) != null) {
System.out.println(this.inputType + " > " + line);
}
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
I'm trying to create a program to find all files by a certain name on a Linux server, and then pipe their absolute paths into an ArrayList for processing. My method of using a Process (with exec) and a BufferedReader seems to have worked for all my other needs (various other commands du -h df-h, etc...) however, it doesn't seem to be working in this case in that I get no data outputted! It does seem to be executing as it takes a minute or two to complete but I never see any data result.
Here is the code: (without try/catch which just prints stack trace)
Process process =
Runtime.getRuntime().exec("find " + Main.serversPath + " -name 'dynmap'");
BufferedReader stdInput = new BufferedReader(
new InputStreamReader(process.getInputStream()));
while ((s = stdInput.readLine()) != null) {
filesToDelete.add(s);
if (Main.debugMode == "High") {
System.out.println("Preprocess: dynmap pass - found " + s);
}
}
process.destroy();
You should always capture the error stream of a process in a separate thread (using a StreamGobbler) to handle the cases where the process throws errors.
class StreamGobbler extends Thread
{
private InputStream is;
private String myMessage;
public StreamGobbler(InputStream istream)
{
this.is = istream;
}
public String getMessage()
{
return this.myMessage;
}
#Override
public void run()
{
StringBuilder buffer = new StringBuilder();
BufferedReader br = new BufferedReader(new InputStreamReader(is));
int size = 1024 * 1024;
char[] ch = new char[size];
int read = 0;
try {
while ((read = br.read(ch, 0, size)) >= 0) {
buffer.append(ch, 0, read);
}
}
catch (Exception ioe) {
ioe.printStackTrace();
}
finally {
try {
br.close();
}
catch (IOException e) {
e.printStackTrace();
}
}
this.myMessage = buffer.toString();
return;
}
}
Then you should use the StreamGobbler to capture error stream as follows:
Process process =
new ProcessBuilder("find", Main.serversPath, "-name", "'dynmap'").start();
StreamGobbler error = new StreamGobbler(process.getErrorStream());
error.start();
BufferedReader stdInput =
new BufferedReader(new InputStreamReader(process.getInputStream()));
while ((s = stdInput.readLine()) != null) {
filesToDelete.add(s);
if (Main.debugMode.equals("High")) {
System.out.println("Preprocess: dynmap pass - found " + s);
}
}
// Get the exit status
int exitStatus = process.waitFor();
if (exitStatus != 0) {
// read the error.getMessage() and handle accordingly.
}
process.destroy();
Also, it is recommended to use the ProcessBuilder to create a process.
This line:
System.out.println("Preprocess: dynmap pass - found " + s);
is likely not getting executed.
When used on Strings the == operator compares the object references instead of the values.
In order to compare the actual values you should use the String.equals() method:
if (Main.debugMode.equals("High")) {
System.out.println("Preprocess: dynmap pass - found " + s);
}
See here for an overview of comparing Objects in Java
I think your problem is that you are not closing your BufferedRreader.
You should ALWAYS close and if nesasary, flush your streams.
So:
Process process = Runtime.getRuntime().exec("find " + Main.serversPath + " -name 'dynmap'");
BufferedReader stdInput = new BufferedReader(new InputStreamReader(process.getInputStream()));
while ((s = stdInput.readLine()) != null) {
filesToDelete.add(s);
if (Main.debugMode == "High") {
System.out.println("Preprocess: dynmap pass - found " + s);
}
}
process.destroy();
//Add this line here!
stdInput.close();
You may also want to change this:
if (Main.debugMode == "High") {
System.out.println("Preprocess: dynmap pass - found " + s);
}
to this:
if (Main.debugMode.equals("High")) {
System.out.println("Preprocess: dynmap pass - found " + s);
}
I am working on an application an have an issue about running shell command from java application. here is the code:
public String execRuntime(String cmd) {
Process proc = null;
int inBuffer, errBuffer;
int result = 0;
StringBuffer outputReport = new StringBuffer();
StringBuffer errorBuffer = new StringBuffer();
try {
proc = Runtime.getRuntime().exec(cmd);
} catch (IOException e) {
return "";
}
try {
response.status = 1;
result = proc.waitFor();
} catch (InterruptedException e) {
return "";
}
if (proc != null && null != proc.getInputStream()) {
InputStream is = proc.getInputStream();
InputStream es = proc.getErrorStream();
OutputStream os = proc.getOutputStream();
try {
while ((inBuffer = is.read()) != -1) {
outputReport.append((char) inBuffer);
}
while ((errBuffer = es.read()) != -1) {
errorBuffer.append((char) errBuffer);
}
} catch (IOException e) {
return "";
}
try {
is.close();
is = null;
es.close();
es = null;
os.close();
os = null;
} catch (IOException e) {
return "";
}
proc.destroy();
proc = null;
}
if (errorBuffer.length() > 0) {
logger
.error("could not finish execution because of error(s).");
logger.error("*** Error : " + errorBuffer.toString());
return "";
}
return outputReport.toString();
}
but when i try to exec command like :
/export/home/test/myapp -T "some argument"
myapp reads "some argument" as two seperated arguments.but I want to read "some argument" as only a argument. when i directly run this command from terminal, it executed successfully. I tried '"some argument"' ,""some argument"" , "some\ argument" but did not work for me. how can i read this argument as one argument.
I recall that the an overload of exec method provides a parameter for the arguments seperately. You need to use that
Yup. Here is it
public Process exec(String[] cmdarray)
throws IOException
Just make the command line and all arguments Seperate elements of the String array
first make a string
String cmd="/export/home/test/myapp -T \"some argument\"";
then run cmd in proc