I am a total newbie in JSPs/Tomcat and to a large extent in Java as well. Here's what I have to do -- when a user clicks a button/visits a URL, I want to launch a Java program (which takes some command line arguments).
I can very easily do
Runtime.exec("C:\\Python27\\python.exe test.py")
OR
Runtime.exec("java -cp %CLASSPATH%;C:\\devprojects HelloWorld"
and this works fine. Where HelloWorld.class just prints "HelloWorld".
However, when I attempt a java program which takes command line arguments, the GET request just hangs doing nothing. I don't know what logs to look for or what could be wrong here. After having spent TWO days on trying various things, I am just about to give up now.
Runtime.exec("java -cp %CLASSPATH%;C:\\devprojects Run --username Blah --password Foo");
What user does Tomcat end up running this java program as? Can I make it to be Administrator? This is on Windows 2008, does UAC interfere with things?
I cannot modify the Run.class here, I HAVE to run it as is and with command line parameters.
Please advise.
One idea: you are relying on the default tokenization of your command line as one complete String, and it is not parsing the last one as you expect. Instead you should use the form of this method that takes a String[], after you have chopped up the command line yourself:
http://docs.oracle.com/javase/6/docs/api/java/lang/Runtime.html#exec(java.lang.String[])
Or, it is waiting for input from you, or waiting for you to read its output. This could explain the hang. Search the internet for the dangers of streams and Runtime.exec().
Consider ProcessBuilder instead.
Remember also that you have to be sure that the executed file dont run "forever", and
if you need to pass some arguments, you could use this:
static String startProcess(String command, String dir) throws IOException {
StringBuffer ret = new StringBuffer();
String[] comm = new String[3];
comm[0] = COMMAND_INTERPRETER[0];
comm[1] = COMMAND_INTERPRETER[1];
comm[2] = command;
long start = System.currentTimeMillis();
try {
//Start process
Process ls_proc = Runtime.getRuntime().exec(comm, null, new File(dir));
//Get input and error streams
BufferedInputStream ls_in = new BufferedInputStream(ls_proc.getInputStream());
BufferedInputStream ls_err = new BufferedInputStream(ls_proc.getErrorStream());
boolean end = false;
while (!end) {
int c = 0;
while ((ls_err.available() > 0) && (++c <= 1000)) {
ret.append(conv2Html(ls_err.read()));
}
c = 0;
while ((ls_in.available() > 0) && (++c <= 1000)) {
ret.append(conv2Html(ls_in.read()));
}
try {
ls_proc.exitValue();
//if the process has not finished, an exception is thrown
//else
while (ls_err.available() > 0)
ret.append(conv2Html(ls_err.read()));
while (ls_in.available() > 0)
ret.append(conv2Html(ls_in.read()));
end = true;
}
catch (IllegalThreadStateException ex) {
//Process is running
}
//The process is not allowed to run longer than given time.
if (System.currentTimeMillis() - start > MAX_PROCESS_RUNNING_TIME)
//this is very important
{
ls_proc.destroy();
end = true;
ret.append("!!!! Process has timed out, destroyed !!!!!");
}
try {
Thread.sleep(50);
}
catch (InterruptedException ie) {}
}
}
catch (IOException e) {
ret.append("Error: " + e);
}
return ret.toString();
}
Related
Thank you for all your help. I tried everything from creating a new Thread. To changing around the way I use the writer.flush() and the writer.newLine(). It seems like the code keeps getting hung up at the while loop, constantly running the Thread.sleep() nonstop. I cannot provide the batch file because it is sensitive information, but the String command variable on top of the code is the path I am using to access the command. Please if you do answer this question please run the code first with test bat file and two input field.
Batch Script:
#ECHO OFF
SET /P _inputname= Please enter an name:
SET /P _inputpassword= Please enter an password:
IF "%_inputpassword%"=="1234" GOTO :they_said_1234
ECHO You entered the wrong password!
pause
GOTO
:they_said_1234
ECHO You entered 1,2,3,4!
pause
Java Code:
import java.io.*;
import java.nio.CharBuffer;
import java.util.ArrayList;
public class command{
public static void main(String[] args) {
//String command="cmd /c d: && cd UPSDATA\\Virtualization Scripts\\EMC ESXi Grab\\EMC-ESXi-GRAB-1.3.7 && GRAB_RUN ";
//String command="cmd /c date";
String command = "cmd /c cd C:\\Users\\HFB2VZN\\Desktop\\folderG";
try {
Process process = Runtime.getRuntime().exec(command);
try (Writer writer = new OutputStreamWriter(process.getOutputStream());
Reader reader = new InputStreamReader(process.getInputStream())) {
CharBuffer buf = CharBuffer.allocate(80);
int tries = 2;
while (process.isAlive()) {
while (reader.ready() && reader.read(buf) > 0) {
//3
System.out.println("buf.flip() ran");
System.out.append(buf.flip());
buf.clear();
}
if (tries-- == 0) {
System.out.println("Destroyed");
process.destroy();
break;
}
//1
writer.write("random");
writer.flush();
while (!reader.ready()) {
//2
System.out.println("while() loop, Thread runs in non stop loop");
Thread.sleep(800);
}
}
}
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
}
}`
Compared to this example, executing a batch file will be much slower and having multiple commands may imply that there’s no available output for some time before the next output is generated. Not considering this may cause the loops get out of sync and after the process terminated, you must not execute the while (!reader.ready()) {…} loop without checking whether the process is still alive.
Since your batch file’s second GOTO lacks a target (it’s likely supposed to branch backwards), this batch file may terminal earlier than intended. Since no-one reads the error channel, this stays unnoticed. That could be the reason for hanging in that loop. Note further, that you are generating one input to the batch file per loop iteration, but have limited the number of iteration to 2 per tries variable. For a batch file expecting three inputs (name, password, pause), that’s too little.
The main problem is, there is no way to detect whether a subprocess is actually waiting for our input. This is what we have to work-around here. But a temporary stopping of producing output does not always imply that the program now waits for input.
I fixed your last GOTO to jump to the beginning of the batch file and used the following code to make two attempts entering the right password on the second.
try {
Process process = Runtime.getRuntime().exec(command);
try(Writer writer = new OutputStreamWriter(process.getOutputStream());
Reader reader = new InputStreamReader(process.getInputStream())) {
CharBuffer buf = CharBuffer.allocate(80);
int tries = 2;
while(process.isAlive()) {
do {
if(!buf.hasRemaining()) {
buf.flip();
buf = CharBuffer.allocate(buf.capacity()*2).put(buf);
}
do {} while(reader.ready() && reader.read(buf) > 0);
if(buf.position() > 0) {
char c = buf.get(buf.position()-1);
if(c==':' || c=='.') break;
}
long deadLine = System.nanoTime() + TimeUnit.SECONDS.toNanos(1);
for(long remaining = 1;
!reader.ready() && process.isAlive() && remaining > 0;
remaining = deadLine - System.nanoTime()) {
LockSupport.parkNanos(Math.min(
TimeUnit.MILLISECONDS.toNanos(100),
remaining));
}
} while(reader.ready());
String input = buf.flip().toString();
buf.clear();
System.out.print(input);
String value;
if(input.endsWith("name:")) value = "aName";
else if(input.endsWith("password:")) value = tries>1? "wrongPassword": "1234";
else {
value = "<any key>";
tries--;
}
System.out.println("<- "+value);
writer.write(value);
writer.flush();
if(tries == 0) {
System.out.println("Destroying");
process.destroy();
break;
}
}
}
} catch(IOException e) {
e.printStackTrace();
}
Of course, if you can’t fix the GOTO statement, you have to provide the right password at the first attempt.
The code above will wait up to one second for the availability of new output, unless it recognizes one of the expected prompts in the output. And it won’t wait when the process is not alive anymore.
I am trying to run a powershell command in eclipse with the following code. The powershell script display the list of installed application on windows. The script works fine when it is executed in powershell. But I am unable to get the output on the console. Could someone please tell me what is the problem here?
import com.profesorfalken.jpowershell.PowerShell;
import com.profesorfalken.jpowershell.PowerShellNotAvailableException;
import com.profesorfalken.jpowershell.PowerShellResponse;
public class TestProcessList {
public static void main(String[] args) throws Exception {
try {
PowerShell powerShell = PowerShell.openSession();
String command = "Get-ItemProperty " +
"HKLM:/Software/Wow6432Node/Microsoft/Windows/CurrentVersion/Uninstall/* " +
"| Select-Object DisplayName, DisplayVersion, Publisher, InstallDate " +
"| Format-Table –AutoSize";
PowerShellResponse response = powerShell.executeCommand(command);
System.out.println("Proceses are:" + response.getCommandOutput());
powerShell.close();
} catch (PowerShellNotAvailableException ex) {
throw new RuntimeException("Failed to run PowerShell", ex);
}
}
}
Probably there are thrown some exceptions. Which in your case are not being re-thrown and are consumed (Bad practice).
Change tthe catch block to:
} catch (PowerShellNotAvailableException ex) {
throw new RuntimeException("Failed to run PowerShell", ex)
}
Then you will see what went wrong including its whole stacktrace and possible causes.
UPDATE:
You are actually using piped commands ("|" in execute command string) inside of single command. It wont't work as pipes are not easy to implement in java.
Try out solution basing on following example for command "ps aux | grep java":
import org.apache.commons.io.IOUtils;
public static void main(String[] args) throws Exception
{
Process p1 = Runtime.getRuntime().exec(new String[] { "ps", "aux" });
InputStream input = p1.getInputStream();
Process p2 = Runtime.getRuntime().exec(new String[] { "grep", "java" });
OutputStream output = p2.getOutputStream();
IOUtils.copy(input, output);
output.close(); // signals grep to finish
List<String> result = IOUtils.readLines(p2.getInputStream());
System.out.println(result);
}
Source: https://stackoverflow.com/a/7226858/1688570
As API of the PowerShell library is not known to me, you have to adapt the example to work with PowerShell library by yourself.
Code From PowerShell.java class.
int closingTime = 0;
while (!closeTask.isDone ()
&& !closeTask.isDone()) {
if (closingTime > MAX_WAIT) {
Logger.getLogger(PowerShell.class.getName()).log(Level.SEVERE, "Unexpected error when closing PowerShell: TIMEOUT!");
break;
}
Thread.sleep(WAIT_PAUSE);
closingTime += WAIT_PAUSE;
and
static final int WAIT_PAUSE = 10;
static final int MAX_WAIT = 2000;
This means your command takes more than 2000 milliseconds to close/complete.
I think adding a custom sleep in your code might help. I am not familiar with JPowerShell , you should take a look at PowerShell class. or try with TouDick's answer.
you have to add some wait time. I was executing some commands using java and was not able to print command output on console even though I was using response.getCommandOutput(). So, I tried below by adding wait:
PowerShellResponse response;
Map<String, String> maxWait = new HashMap<String, String>();
maxWait.put("maxWait", "300000");
PowerShell powerShell = PowerShell.openSession().configuration(maxWait);
response = powerShell.executeCommand(yourCommand);
System.out.println(response.getCommandOutput());
The wait time is to wait for maximum time.
what I would like to do is run a batch file multiple times from a java application. Therefore I set up a for-loop that runs this code n times:
for (int i = 0; i < n; i++) {
Runtime.getRuntime().exec("cmd /c start somefile.bat");
}
The problem is that now each time the command is run a new cmd window pops up. However, what I want is just one window that pops up at the beginning and that is used to display all data from the following command calls.
How can I do that?
With && you can execute more than one commands, one after another:
Runtime.getRuntime().exec("cmd /c \"start somefile.bat && start other.bat && cd C:\\test && test.exe\"");
Using multiple commands and conditional processing symbols
You can run multiple commands from a single command line or script using conditional processing symbols. When you run multiple commands with conditional processing symbols, the commands to the right of the conditional processing symbol act based upon the results of the command to the left of the conditional processing symbol.
For example, you might want to run a command only if the previous command fails. Or, you might want to run a command only if the previous command is successful.
You can use the special characters listed in the following table to pass multiple commands.
& [...] command1 & command2
Use to separate multiple commands on one command line. Cmd.exe runs the first command, and then the second command.
&& [...] command1 && command2
Use to run the command following && only if the command preceding the symbol is successful. Cmd.exe runs the first command, and then runs the second command only if the first command completed successfully.
|| [...] command1 || command2
Use to run the command following || only if the command preceding || fails. Cmd.exe runs the first command, and then runs the second command only if the first command did not complete successfully (receives an error code greater than zero).
( ) [...] (command1 & command2)
Use to group or nest multiple commands.
; or , command1 parameter1;parameter2
Use to separate command parameters.
I would use Java's ProcessBuilder or another class which simulates/uses a shell. The following snippet demonstrates the idea (for Linux with bash).
import java.util.Scanner;
import java.io.*;
public class MyExec {
public static void main(String[] args)
{
//init shell
ProcessBuilder builder = new ProcessBuilder( "/bin/bash" );
Process p=null;
try {
p = builder.start();
}
catch (IOException e) {
System.out.println(e);
}
//get stdin of shell
BufferedWriter p_stdin =
new BufferedWriter(new OutputStreamWriter(p.getOutputStream()));
// execute the desired command (here: ls) n times
int n=10;
for (int i=0; i<n; i++) {
try {
//single execution
p_stdin.write("ls");
p_stdin.newLine();
p_stdin.flush();
}
catch (IOException e) {
System.out.println(e);
}
}
// finally close the shell by execution exit command
try {
p_stdin.write("exit");
p_stdin.newLine();
p_stdin.flush();
}
catch (IOException e) {
System.out.println(e);
}
// write stdout of shell (=output of all commands)
Scanner s = new Scanner( p.getInputStream() );
while (s.hasNext())
{
System.out.println( s.next() );
}
s.close();
}
}
Please note that it is only a snippet, which needs to be adapted for Windows, but in general it should work with cmd.exe.
public void TestCommandRun(){
Process process = null;
String[] command_arr = new String[]{"cmd.exe","/K","start"};
ProcessBuilder pBuilder = new ProcessBuilder("C:/Windows/System32/cmd.exe");
try{
process = pBuilder.start();
}
catch(IOException e){
e.printStackTrace();
System.out.println("Process failed");
}
if(null != process){
OutputStream out = process.getOutputStream();
OutputStreamWriter outWriter = new OutputStreamWriter(out);
BufferedWriter bWriter = new BufferedWriter(outWriter);
try{
bWriter.write("dir");
bWriter.newLine();
bWriter.write("ipconfig");
bWriter.flush();
bWriter.close();
}
catch(IOException e){
e.printStackTrace();
System.out.println("bWriter Failed");
}
}
}
I've got some code that uses Runtime.exec() to run an external .jar (built as an IzPack installer).
If I run this external.jar from the command line like so:
java -jar external.jar
Then the command prompt does not return control until the application is finished. However, if I run external.jar from within some java class, using:
Process p = Runtime.getRuntime().exec("java -jar external.jar");
int exitCode = p.waitFor();
System.out.println("Process p returned: " + exitCode);
Then p returns almost instantly with a success code of 0, despite external.jar having not yet completed execution (i've also tried this via the ProcessBuilder route of external file execution).
Why does it wait to return from the command line, but not when executed from within another java program?
I've also set up 3 jars, A, B and C where A calls B which calls C (using Runtime.exec()), where C Thread.sleeps for 10 seconds, as a simple test, and as expected, A doesn't return until 10 seconds after it runs.
I figure this is probably some kind of a threading issue with external.jar where execution is being handed over from one thing to another, but given that it works directly from the command line i kind of expected to see the same behaviour (perhaps naively) when called from within another java program.
I've tested this on Windows and Ubuntu with Java 6.
Thanks!
another possible way to achieve this might be to capture the output of the process and wait for it to finish.
For example:
Process tr = Runtime.getRuntime().exec( new String[]{"wkhtmltopdf",mainPage,mainPagePDF});
BufferedReader stdOut=new BufferedReader(new InputStreamReader(tr.getInputStream()));
String s;
while((s=stdOut.readLine())!=null){
//nothing or print
}
Normally the output stream is tr.getInputStream() but depending on the program you are executing the process output stream migh be:
tr.getInputStream()
tr.getErrorStream()
tr.getOutputStream()
By doing this while loop you force your program to wait the process to finish.
You can use Process Builder....
ProcessBuilder pb = new ProcessBuilder("java", "-jar", "/fielname.jar");
Process p = pb.start();
p.waitFor();
Are you spawning a new thread to handle the spawning of the process? If so the origional program will continue to operate independently of the spawned process and therefore waitFor() will only work on the new process and not the parent.
Process.waitFor() is useless for some native system command.
You need to get the process's output to determine if it is returned.
I wrote a sample code for you
/**
*
* #param cmdarray command and parameter of System call
* #param dir the directory execute system call
* #param returnImmediately true indicate return after system call immediately;
* false otherwise.
* if set true, the returned call result does not have reference value
* #return the return code of system call , default is -1
*/
public static int systemCall(String[] cmdarray,File dir,boolean returnImmediately)
{
int result = -1;
try {
Process p = Runtime.getRuntime().exec(cmdarray,null,dir);
if(!returnImmediately)
{
java.io.InputStream stdin = p.getInputStream();
java.io.InputStreamReader isr = new java.io.InputStreamReader(stdin);
java.io.BufferedReader br = new java.io.BufferedReader(isr);
String line = null;
while ( (line = br.readLine()) != null)
System.out.println(line);
}
try{result = p.exitValue();}
catch(Exception ie){;}
} catch (IOException e) {
e.printStackTrace();}
return result;
}
public static void main(String[] argc){
String[] cmdarray = {"jar","cvf","s2.jar","*"};
File dir = new File("D:\\src\\struts-2.3.1");
int k = systemCall(cmdarray,dir,true);
System.out.println("k="+k);
}
I had the same problem using processs to execute some software using the console, and i just solved it using process.waitFor()
For me it worked perfectly.
try{
Process tr = Runtime.getRuntime().exec( new String[]{ "wkhtmltopdf",frontPage,frontPagePDF});
tr.waitFor();
} catch (Exception ex) {
EverLogger.logEntry("Error al pasar a PDF la portada", "error", "activity");
return;
}
some more code here.
I am trying to run a batch file with Runtime.exec() and then output its InputStream into a JTextArea. What I have works, but only partially. What happens is the batch file runs, but if it executes a command other than something like "echo" that command immediately terminates and the next line executes. For example, let's say I try to run a simple batch file like this:
#echo off
echo hello. waiting 5 seconds.
timeout /t 5 /nobreak > NUL
echo finished. goodbye.
The batch file executes, and the JTextArea says
hello. waiting 5 seconds.
finished. goodbye.
but it doesn't wait for 5 seconds in the middle.
I can't figure out why it's doing this. Here's what I use to run the batch file and read its InputStream.
private class ScriptRunner implements Runnable {
private final GUI.InfoGUI gui; // the name of my GUI class
private final String script;
public ScriptRunner(final GUI.InfoGUI gui, final File script) {
this.gui = gui;
this.script = script.getAbsolutePath();
}
#Override
public void run() {
try {
final Process p = Runtime.getRuntime().exec(script);
StreamReader output = new StreamReader(p.getInputStream(), gui);
Thread t = new Thread(output);
t.start();
int exit = p.waitFor();
output.setComplete(true);
while (t.isAlive()) {
sleep(500);
}
System.out.println("Processed finished with exit code " + exit);
} catch (final Exception e) {
e.printStackTrace();
}
}
}
private class StreamReader implements Runnable {
private final InputStream is;
private final GUI.InfoGUI gui;
private boolean complete = false;
public StreamReader(InputStream is, GUI.InfoGUI gui) {
this.is = is;
this.gui = gui;
}
#Override
public void run() {
BufferedReader in = new BufferedReader(new InputStreamReader(is));
try {
while (!complete || in.ready()) {
while (in.ready()) {
gui.setTextAreaText(in.readLine() + "\n");
}
sleep(250);
}
} catch (final Exception e) {
e.printStackTrace();
} finally {
try {
in.close();
} catch (final Exception e) {
e.printStackTrace();
}
}
public void setComplete(final boolean complete) {
this.complete = complete;
}
}
public void sleep(final long ms) {
try {
Thread.sleep(ms);
} catch (final InterruptedException ie) {
}
}
I know my code is pretty messy, and I'm sure it contains grammatical errors.
Thanks for anything you can do to help!
You're creating a Process but you're not reading from its standard error stream. The process might be writing messages to its standard error to tell you that there's a problem, but if you're not reading its standard error, you won't be able to read these messages.
You have two options here:
Since you already have a class that reads from a stream (StreamReader), wire up another one of these to the process's standard error stream (p.getErrorStream()) and run it in another Thread. You'll also need to call setComplete on the error StreamReader when the call to p.waitFor() returns, and wait for the Thread running it to die.
Replace your use of Runtime.getRuntime().exec() with a ProcessBuilder. This class is new in Java 5 and provides an alternative way to run external processes. In my opinion its most significant improvement over Runtime.getRuntime().exec() is the ability to redirect the process's standard error into its standard output, so you only have one stream to read from.
I would strongly recommend going for the second option and choosing to redirect the process's standard error into its standard output.
I took your code and replaced the line
final Process p = Runtime.getRuntime().exec(script);
with
final ProcessBuilder pb = new ProcessBuilder(script);
pb.redirectErrorStream(true);
final Process p = pb.start();
Also, I don't have your GUI code to hand, so I wrote the output of the process to System.out instead.
When I ran your code, I got the following output:
hello. waiting 5 seconds.
ERROR: Input redirection is not supported, exiting the process immediately.
finished. goodbye.
Processed finished with exit code 0
Had you seen that error message, you might have twigged that something was up with the timeout command.
Incidentally, I noticed in one of your comments that none of the commands suggested by ughzan worked. I replaced the timeout line with ping -n 5 127.0.0.1 > NUL and the script ran as expected. I couldn't reproduce a problem with this.
The problem is definitely in timeout.exe. If you add echo %errorlevel% after line with timeout, you will see that it returns 1 if running from java. And 0 if running in usual way. Probably, it requires some specific console functionality (i.e. cursor positioning) that is suppressed when running from java process.
Is there anything I can do to get this to work while running from Java
If you don't need ability to run any batch file then consider to replace timeout with ping. Otherwise... I've tried to run batch file with JNA trough Kernel32.CreateProcess and timeout runs fine. But then you need to implement reading of process output trough native calls also.
I hope someone will suggest better way.
The ready method only tells if the stream can guarantee that something can be read immediately, without blocking. You can't really trust it because always returning false is a valid implementation. Streams with buffers may return true only when they have something buffered. So I suspect your problem is here:
while (!complete || in.ready()) {
while (in.ready()) {
gui.setTextAreaText(in.readLine() + "\n");
}
sleep(250);
}
It should rather read something like this:
String line;
while (!complete || (line=in.readLine()) != null) {
gui.setTextAreaText(line + "\n");
}
It's probably because your "timeout ..." command returned with an error.
Three ways to test it:
Check if the "timeout ..." command works in the Windows command prompt.
Replace "timeout ..." in the script with "ping -n 5 127.0.0.1 > NUL" (it essentially does the same thing)
Remove everything but "timeout /t 5 /nobreak > NUL" from your script. The process should return with an error (1) if the timeout failed because it is the last command executed.