I'm aware how to open an exe program with parameters in Java from finding the answer online. However my specific shortcut is a bit complicated for me to understand.
I'm trying to open a shortcut which has the following target:
C:\Windows\System32\javaw.exe -cp jts.jar;total.2012.jar -Dsun.java2d.noddraw=true -Dswing.boldMetal=false -Dsun.locale.formatasdefault=true -Xmx768M -XX:MaxPermSize=128M jclient/LoginFrame C:\Jts
In my program I've split up the location and what I think are the parameters. However when I run the program I get the error 'Could not create Java Virtual Machine, Program will Exit'. Can someone with a better understanding of whats going on explain what I might be doing wrong or point me in a direction where I can read up?
String location = "C:\\Windows\\System32\\javaw.exe";
String p1="-cp jts.jar;total.2012.jar";
String p2="-Dsun.java2d.noddraw=true";
String p3="-Dswing.boldMetal=false";
String p4="-Dsun.locale.formatasdefault=true";
String p5="-Xmx768M";
String p6="-XX:MaxPermSize=128M";
String p7="jclient/LoginFrame" ;
String p8 = "C:\\Jts";
try {
Process p = new ProcessBuilder(location,p1,p2,p3,p4,p5,p6,p7,p8).start();
} catch (IOException ex) {
Logger.getLogger(Openprogramtest.class.getName()).log(Level.SEVERE, null, ex);
}
Each String you pass to ProcessBuilder is a separate argument (except the first one, which is the command).
Think of it like the args[] which are passed to your main method. Each String would be a separate element in the array.
I suspect that p1 is been interpreted as a single argument, when it should actually be two...
Try separating this argument into two separate parameters
String location = "C:\\Windows\\System32\\javaw.exe";
String p1="-cp";
String p2="jts.jar;total.2012.jar";
String p3="-Dsun.java2d.noddraw=true";
String p4="-Dswing.boldMetal=false";
String p5="-Dsun.locale.formatasdefault=true";
String p6="-Xmx768M";
String p7="-XX:MaxPermSize=128M";
String p8="jclient/LoginFrame" ;
String p9 = "C:\\Jts";
Amendment
Look at the -cp parameter, it appears that the class path elements are relative to the location that the command is executed. This suggests that you need to use the ProcessBuilder#directory(File) to specify the location that the command should executed from.
For example, if you program is installed in C:\Program Files\MyAwesomeApp, but you run it from the context of C:\Desktop, then Java won't be able to find the Jar files it needs, generally raising a ClassNotFound exception.
Instead, you need to tell ProcessBuilder that you want the command to executed from within the C:\Program Files\MyAwesomeApp context.
For example...
ProcessBuilder pb = new ProcessBuilder(...);
pb.directory(new File("C:\Program Files\MyAwesomeApp"));
// Other settings...
Process p = pb.start();
Updated from running example
Just to make the point. I built myself a little Java program that simple printed a simple message to the standard out.
When I run this, it works as expected...
try {
String params[] = new String[]{
"C:\\Windows\\System32\\javaw.exe",
"-cp",
"C:\\...\\TestSimpleProcessBuilder\\build\\classes",
"-Dsun.java2d.noddraw=true",
"-Dswing.boldMetal=false",
"-Dsun.locale.formatasdefault=true",
"-Xmx768M",
"-XX:MaxPermSize=128M",
"testsimpleprocessbuilder/HelloWorld",
"Boo"
};
ProcessBuilder pb = new ProcessBuilder(params);
pb.redirectErrorStream();
Process p = pb.start();
InputStream is = p.getInputStream();
int in = -1;
while ((in = is.read()) != -1) {
System.out.print((char) in);
}
is = p.getErrorStream();
in = -1;
while ((in = is.read()) != -1) {
System.out.print((char) in);
}
System.out.println("p exited with " + p.exitValue());
} catch (IOException ex) {
Logger.getLogger(TestSimpleProcessBuilder.class.getName()).log(Level.SEVERE, null, ex);
}
When I change the arguments from
"-cp",
"C:\\...\\TestSimpleProcessBuilder\\build\\classes",
to
"-cp C:\\...\\TestSimpleProcessBuilder\\build\\classes",
It fails with...
And outputs
Unrecognized option: -cp
C:\DevWork\personal\java\projects\wip\StackOverflow\TestSimpleProcessBuilder\build\classes
And if you're wondering, this is the little test program I wrote that gets run...
package testsimpleprocessbuilder;
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello world - world says " + (args.length > 0 ? args[0] : "Nothing"));
}
}
Related
I have simple java program for compiling java classes.
I created a JAR of this program and when I run it on Ubuntu I pass to the jar the path of folder with java files.
public class Main {
public static void main(String[] args) throws IOException, InterruptedException {
compile(args[0]);
}
//pathToFiles - is a value from command line arguments
private static void compile(String pathToFiles) throws IOException, InterruptedException {
List<String> cmdList = new ArrayList<>();
cmdList.add("javac");
cmdList.add(pathToFiles);
System.out.println("cmd: "+cmdList);
ProcessBuilder pb = new ProcessBuilder(cmdList);
Process process = pb.start();
int exitValue = process.waitFor();
if (exitValue != 0) {
generateCompileException(process);
}
}
//method just generates error message if there was an error
private static void generateCompileException(Process process){
StringBuilder response = new StringBuilder();
try (final BufferedReader b = new BufferedReader(new InputStreamReader(process.getErrorStream()))) {
String line;
if ((line = b.readLine()) != null)
response.append(line);
} catch (final IOException e) {
e.printStackTrace();
}
throw new RuntimeException(response.toString());
}
}
When I pass path containing single java file it works:
java -jar co-1.jar /home/admin/test2/Calculator.java
But I want to compile multiple files. When I pass path containing multiple files I get error: file not found.
java -jar co-1.jar '/home/admin/test2/*.java'
PS: If I run a javac command manually with multiple files, it will work:
###################################
UPDATE:
I've added bash command to ProcessBuilder:
private static void compile(String pathToFiles) throws IOException, InterruptedException {
List<String> cmdList = new ArrayList<>();
cmdList.add("bash");
cmdList.add("-c");
cmdList.add("javac");
cmdList.add(pathToFiles);
System.out.println("Processor builder command: "+cmdList);
ProcessBuilder pb = new ProcessBuilder(cmdList);
Process process = pb.start();
int exitValue = process.waitFor();
if (exitValue != 0) {
System.out.println("Finished with error. Exit value: "+exitValue);
generateCompileException(process);
}
}
But process withished with error code 2 with empty response from ProcessBuilder.
PS: RuntimeException was thrown by this line: throw new RuntimeException(response.toString());
ProcessBuilder will not evaluate wildcards, as that is a feature of your terminal (such as bash). If you want wildcard to be expanded you need to run bash inside ProcessBuilder command, such as:
String commandContainingWildcard = "javac /blah/*.java";
ProcessBuilder pb = new ProcessBuilder("bash", "-c", commandContainingWildcard);
... // start() etc
For the above to work you need to have "bash" or whatever shell you use in your path, otherwise you will need to use full path to bash (such as "/bin/bash").
The third argument for command to compile must exactly match what works inside your terminal and must be the entire value not "javac" followed by wildcard. Remove single quotes around *.java (so that ProcessBuilder is provided with three command line parameters, not four or more).
However I suggest that ProcessBuilder with bash isn't the best way to do this work. You could try Java compiler tool interface, and get rid of wildcard by easy use of Files.find(dir, 1, (p,a) -> p.getFileName().toString().endsWith(".java")) to scan for all java files and join the paths explicitly for compilation.
UPDATE
Having now resolved your problem you may now find that the javac process fails / freezes due the incorrect way you read the stderr stream - this needs to happen at same time as stdout and before process.waitFor(). An easy fix is to consume stdout+stderr together:
ProcessBuilder pb = new ProcessBuilder(cmdList);
pb.redirectErrorStream(true);
Process process = pb.start();
ByteArrayOutputStream response = new ByteArrayOutputStream();
process.getInputStream().transferTo(response);
int exitValue = process.waitFor();
if (exitValue != 0) {
System.out.println("Finished with error. Exit value: "+exitValue);
throw new RuntimeException(new String(response.toByteArray()));
}
Remove quotes and use the command as below.
java -cp co-1.jar:/home/admin/test2/* Main.class <args>
See also
https://docs.oracle.com/javase/7/docs/technotes/tools/windows/classpath.html
PS: Unix uses :(colon) as delimiter and windows uses ;(semi-colon) delimiter to separate multiple paths.
I am getting an exception like java.io.IOException: Cannot run program cat /home/talha/* | grep -c TEXT_TO_SEARCH": error=2, No such file or directory while executing the command below despite that there are no issues when I execute the same command through the terminal. I need to execute and return the output of the command below:
cat /home/talha/* | grep -c TEXT_TO_SEARCH
Here is the method used to execute commands using Runtime class:
public static String executeCommand(String command) {
StringBuffer output = new StringBuffer();
Process p;
try {
p = Runtime.getRuntime().exec(command);
p.waitFor();
BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line = "";
while ((line = reader.readLine()) != null) {
output.append(line + "\n");
}
} catch (Exception e) {
e.printStackTrace();
}
return output.toString();
}
Runtime.exec does not use a shell (like, say, /bin/bash); it passes the command directly to the operating system. This means wildcards like * and pipes (|) will not be understood, since cat (like all Unix commands) does not do any parsing of those characters. You need to use something like
p = new ProcessBuilder("bash", "-c", command).start();
or, if for some bizarre reason you need to stick to using the obsolete Runtime.exec methods:
p = Runtime.getRuntime().exec(new String[] { "bash", "-c", command });
If you are only running that cat/grep command, you should consider abandoning the use of an external process, since Java code can easily traverse a directory, read lines from each file, and match them against a regular expression:
Pattern pattern = Pattern.compile("TEXT_TO_SEARCH");
Charset charset = Charset.defaultCharset();
long count = 0;
try (DirectoryStream<Path> dir =
Files.newDirectoryStream(Paths.get("/home/talha"))) {
for (Path file : dir) {
count += Files.lines(file, charset).filter(pattern.asPredicate()).count();
}
}
Update: To recursively read all files in a tree, use Files.walk:
try (Stream<Path> tree =
Files.walk(Paths.get("/home/talha")).filter(Files::isReadable)) {
Iterator<Path> i = tree.iterator();
while (i.hasNext()) {
Path file = i.next();
try (Stream<String> lines = Files.lines(file, charset)) {
count += lines.filter(pattern.asPredicate()).count();
}
};
}
$PATH is an environment variable that tells the system where to search for executable programs (it's a list of directories separated by colons). It is usually set in your .bashrc or .cshrc file but this is only loaded when you log in. When Java runs, $PATH is likely not set because the rc file is not executed automatically, so the system can't find programs without specifying exactly where they are. Try using /bin/cat or /usr/bin/cat instead of just cat and see if it works. If it does, $PATH is your problem. You can add $PATH=/bin:/usr/bin to your script or just leave it with the directory name specified (e.g. /bin/cat).
Just because you can execute it in a login session doesn't mean it will work the same when a daemon like your Java program runs. You have to know what's in your .bashrc or .cshrc file and even sometimes how the system file is written (/etc/bashrc) in order to know how to write a script that runs under a daemon. Another consideration is that daemons often run under the context of a different user, and that throws things off, too.
I am trying to install missing dependencies on a Linux for a program I am making. I however am failing at getting the root access required to install the missing dependencies. Here is what I have so far:
My logic is as follows:
1) Check if the dependency is installed using pacapt (npm in this case)
2) if so then get the user password using a text prompt
3) then continue further instructions like so: echo [userpass] | sudo -S ...
Right now the 'echo [userpass] | sudo -S ...' command gets printed out to the shell like so; [userpass] | sudo -S ... (where the user password is displayed in place of [userpass]), but does not execute.
And here is my code:
public class LinuxDependencyCheck extends Application{
public static void main (String [] args){
launch(args);
}
#Override
public void start(Stage mainWindow){
String userPass = null;
String terminalOut = null;
terminalOut = runBash("./LinuxScripts/pacapt -Qqe npm");
if (terminalOut.equals("npm")){
userPass = getUserPass();
if (userPass != null){
System.out.println("runing");
runBash("echo " + userPass + " | sudo -S npm install" +
" phantomjs2");
}
}
}
public String runBash(String runCommand){
String result = null;
String returnVal = null;
try {
Runtime r = Runtime.getRuntime();
Process p = r.exec(runCommand);
BufferedReader in =
new BufferedReader(new InputStreamReader(p.getInputStream()));
String inputLine;
while ((inputLine = in.readLine()) != null) {
System.out.println(inputLine);
result += inputLine;
returnVal = inputLine;
}
in.close();
} catch (IOException e) {
System.out.println(e);
}
return returnVal;
}
public String getUserPass(){
TextInputDialog dialog = new TextInputDialog("Password");
dialog.setTitle("Installation helper");
dialog.setHeaderText("It looks like you are missing" +
" dependecies to complete this action" +
" would you like to try to install" +
" them now");
dialog.setContentText("Please enter your password :");
// Traditional way to get the response value.
Optional<String> result = dialog.showAndWait();
if (result.isPresent()){
return result.get().toString();
}
return result.get();
}
}
Your runBash() method is poorly named, as it does nothing to cause the given command to be run via bash. It is therefore also inappropriate for use with a command string such as you are specifying, which relies on the shell's pipe operator to string two separate commands together.
When you do this ...
runBash("echo " + userPass + " | sudo -S npm install" +
" phantomjs2");
... Java splits the string on whitespace, takes the first substring ("echo") as the command, and executes that command with all the other substrings strings as arguments. Needless to say, that will run without error, but also without the effect you intended.
If you really want to execute the command string via bash (as it appears you do), then in your runBash() method you could change this ...
Process p = r.exec(runCommand);
... to this ...
Process p = r.exec("/bin/bash", "-c", runCommand);
. That should at least get you past your first hurdle.
You also should close the Process's OutputStream (by which you could have piped data into the process), and drain the Process's error stream. In general, you need to drain the input and error streams in parallel, because if either one's buffer fills up then the process can block. Perhaps that's not a risk for this particular command, but you'll need to judge. It's also good form to waitFor() the Process; doing so may avoid Java accumulating zombie child processes.
Here's the situation. Im creating a UI which will allow make using a genetic programming system (ECJ) easier to use.
Currently you need to run a command prompt within the ECJ folder and use the commands similar to this to execute a parameter file.
java ec.Evolve -file ec\app\tutorial5\tutorial5.params
Where the full path of tutorial5 is
C:\Users\Eric\Documents\COSC\ecj\ec\app\tutorial5\tutorial5.params
and the command prompt must be executed from
C:\Users\Eric\Documents\COSC\ecj
My program makes the user select a .params file (which is located in a ecj subdirectory) and then use the Runtime.exec() to execute
java ec.Evolve -file ec\app\tutorial5\tutorial5.params
What i have so far
// Command to be executed
String cmd = "cd " + ecjDirectory;
String cmd2 = "java ec.Evolve -file " + executeDirectory;
System.out.println(cmd);
try {
Process p = Runtime.getRuntime().exec(
new String[]{"cmd.exe", "/c", cmd, cmd2});
BufferedReader r = new BufferedReader(new InputStreamReader(p.getInputStream()));
statusTF.append(r.readLine());
p.waitFor();
} catch (IOException | InterruptedException ex) {
System.out.println("FAILED: " + ex.getMessage());
statusTF.append("Failed\n");
}
Currently it outputs the change directory command but nothing else.
Can this be done?
First, the 'cd' command can't be executed by Runtime.exec() in the first place (see How to use "cd" command using Java runtime?). You should be able to just set the working directory for the process when you call exec (see below).
Second, running 'cmd.exe /c' to execute your process isn't what you want here. You won't be able to get the results of your process running, because that is returned to the command window -- which eats the error and then closes without passing the error along to you.
Your exec command should look more like this:
Process p = Runtime.getRuntime().exec(
command, null, "C:\Users\Eric\Documents\COSC\ecj");
Where 'command' looks like this:
String command = "java ec.Evolve -file ec\app\tutorial5\tutorial5.params"
Edit: For reading error messages, try this:
String error = "";
try (InputStream is = proc.getErrorStream()) {
error = IOUtils.toString(is, "UTF-8");
}
int exit = proc.waitFor();
if (exit != 0) {
System.out.println(error);
} else {
System.out.println("Success!");
}
You can use Java processbuilder:
processBuilder documentation!
you can define the working directory of the process and all other stuff.
Each call to exec() runs in a new environment, this means that the call to cd will work, but will not exist to the next call to exec().
I prefer to use Apache's Commons Exec, it's provides an excellent facade over Java's Runtime.exec() and gives a nice way to specify the working directory. Another very nice thing is they provide utilities to capture standard out and standard err. These can be difficult to properly capture yourself.
Here's a template I use. Note that this sample expects an exit code of 0, your application may be different.
String sJavaPath = "full\path\to\java\executable";
String sTutorialPath = "C:\Users\Eric\Documents\COSC\ecj\ec\app\tutorial5\tutorial5.params";
String sWorkingDir = "C:\Users\Eric\Documents\COSC\ecj";
try (
OutputStream out = new ByteArrayOutputStream();
OutputStream err = new ByteArrayOutputStream();
)
{
// setup watchdog and stream handler
ExecuteWatchdog watchdog = new ExecuteWatchdog(Config.TEN_SECONDS);
PumpStreamHandler streamHandler = new PumpStreamHandler(out, err);
// build the command line
CommandLine cmdLine = new CommandLine(sJavaPath);
cmdLine.addArgument("ec.Evolve");
cmdLine.addArgument("-file");
cmdLine.addArgument(sTutorialPath);
// create the executor and setup the working directory
Executor exec = new DefaultExecutor();
exec.setExitValue(0); // tells Executor we expect a 0 for success
exec.setWatchdog(watchdog);
exec.setStreamHandler(streamHandler);
exec.setWorkingDirectory(sWorkingDir);
// run it
int iExitValue = exec.execute(cmdLine);
String sOutput = out.toString();
String sErrOutput = err.toString();
if (iExitValue == 0)
{
// successful execution
}
else
{
// exit code was not 0
// report the unexpected results...
}
}
catch (IOException ex)
{
// report the exception...
}
How can I make the following run?
public class ExecTest {
public static void main(String[] args) {
try {
//Notice the multiple spaces in the argument
String[] cmd = {"explorer.exe", "/select,\"C:\\New Folder\\file.txt\""};
//btw this works
//String cmd = "explorer.exe /select,\"C:\\New Folder\\file.txt\"";
//and surprisingly this doesn't work
//String[] cmd = {"explorer.exe", "/select,\"C:\\New Folder\\file.txt\""};
//Update: and (as crazy as it seems) the following also worked
//String[] cmd = {"explorer.exe", "/select,\"C:\\New", "Folder\\file.txt\""};
Runtime.getRuntime().exec(cmd);
} catch (Exception e) {
e.printStackTrace();
}
}
}
Using Java 6. Tested under Vista x64. By the way, taking the string that gets executed (you'll have to use the String version of exec to get it) and using it in the Search field of Vista's start menu will run as expected.
Ok, this is not simply an update but also an answer so I'm filing it as one. According to all information I could find, the following should theoretically do it:
String[] cmd = {"explorer.exe", "/select,\"C:\New", "", "", "", "", "", "", "Folder\file.txt\""};
The multiple spaces have been broken into empty strings and the array version of exec is used.
Using the above array, I debugged the loop in lines 50-75 of java.lang.ProcessImpl where a string is finally constructed. The resulting string was:
explorer.exe /select,"C:\New Folder\file.txt"
This is what is passed as the 1st argument to ProcessImpl's native create method (line 118 same class), which as it seems fails to run properly this command.
So I guess it all ends here... sadly.
Thnx prunge for pointing out the java bug.
Thnx everyone for their time and interest!
A miracle, it works!
Don't ask me why, but when i, after quite a while of nerve-wrecking research in the internets, was close to give up and use a temporary batch file as a workaround, i forgot to add the /select, parameter to the command, and, who would have thought, the following works on my Win 7 32Bit System.
String param = "\"C:\\Users\\ME\\AppData\\Local\\Microsoft\\Windows\\Temporary Internet Files\\\"";
try {
String[]commands = new String[]{"explorer.exe", param};
Process child = Runtime.getRuntime().exec(commands);
} catch (IOException e1) {
System.out.println("...");
}
General Solution:
The solution of the bug-database mentioned by prunge in his post (http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6511002) worked fine for me.
Reason:
Apparently the problem lies with the commenting of some characters done by java which it does before actually executing the command string.
You have to do the commenting yourself by tokenizing your command string, to prevent the faulty java one to spring into action and mess everything up.
How to fix:
So, in my case i had to do the following (tokenizing my command string, so that no spaces are left inside the string):
String param[] = {
"explorer.exe",
"/select,C:\\Users\\ME\\AppData\\Local\\Microsoft\\Windows\\Temporary",
"Internet",
"Files\\"};
try {
Process child = Runtime.getRuntime().exec(param);
} catch (IOException e1) {
System.out.println("...");
}
As you can see i basically started a new String wherever a space occured, so "Temporary Internet Files" became "Temporary","Internet","Files".
Always use Runtime.exec(String[]), not Runtime.exec(String) unless the command line is extremely simple.
Use new File(pathName).canExecute() first to check whether it's executable or not
EDIT:
public static void runAll(String... cmd)
{
for(String s : cmd)
{
try
{
Runtime.getRuntime().exec(cmd);
}
catch(Exception e)
{
e.printStackTrace();
}
}
}
and then you can use it like: runAll("explorer.exe", "taskmgr.exe");
The characters ,-& and double spaces, all combined are a nightmare!
All the answers exposed here failed for "\\NAS\media\Music\Artistes\E\Earth, Wind & Fire\1992 - The eternal dance - Vol. 1 (1971-1975) (double space between 'Vol. 1' and '(1971').
I have no other choice than writing a temporary batch file:
void openFolderOf( Album album ) {
try {
final String path = album._playList.getParent();
final File batch = File.createTempFile( getClass().getSimpleName(), ".bat" );
try( PrintStream ps = new PrintStream( batch )) {
ps.println( "explorer.exe \"" + path + '"' );
}
Runtime.getRuntime().exec( batch.getAbsolutePath());
}
catch( final Throwable t ) {
t.printStackTrace();
}
}
Note: on cmd.exe, the line explorer "\\NAS..." works well but not with Runtime.exec() nor ProcessBuilder.
Could be a Java bug. See:
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6511002
Did a bit of debugging out of curiosity, I think things are becoming unstuck in java.lang.ProcessImpl (see the constructor). Noticed that when it got to actually calling the underlying Windows API the string had turned into
explorer.exe "/select,"c:\New Folder\test.txt""
So that might explain why, as for workarounds see the bug database link.
For your specific case of needing the reveal/select command, I get around the windows quote nightmare by using cmd /c start:
String[] cmd = {"cmd", "/c", "start explorer.exe /select," + path};
Where path is the absolute path from a File object.
A better way to do it would be using ProcessBuilder object:
Process p;
p = new ProcessBuilder("/Applications/Sublime Text.app/Contents/MacOS/sublime_text", homeDir + _CURL_POST_PUT_CMDS).start();
int exitValue = p.waitFor();
if (exitValue != 0){
System.out.println("Error to open " + homeDir + _CURL_POST_PUT_CMDS);
}
Simple way to resolve this problem for files is java.awt.Desktop Since 1.6
Example:
Desktop.getDesktop().open(new File(fullFileName));