I am trying to do something using system exec in Java
Runtime.getRuntime().exec(command);
Surprisingly everything that is related with paths, directories and files is not working well
I don't get why and just want to know is there any alternatives?
The alternative is to use the ProcessBuilder class, which has a somewhat cleaner interface, but your main problem is probably related to how the OS processes command lines, and there isn't much Java can do to help you with that.
As noted above, cd is a shell builtin. i.e. it's not an executable. You can determine this using:
$ which cd
cd: shell built-in command
As it's not a standalone executable, Runtime.exec() won't be able to do anything with it.
You may be better off writing a shell script to do the shell-specific stuff (e.g. change the working directory) and then simply execute that shell script using Runtime.exec(). You can set PATH variables etc. within your script and leave Java to simply execute your script.
One thing that catches people out is that you have to consume your script's stdout/stderr (even if you throw it away). If you don't do this properly your process will likely block. See this SO answer for more details.
The exec() method can take three arguments. The third is the directory your subprocess should use as its working directory. That solves your "cd" problem, anyway.
Related
My question consists of several things I don't understand about the use of "cmd.exe", "/c" when executing sub-processes from Java in Windows. Basically, I couldn't find a good explanation about when and why they're needed.
My specific problems: I have a small framework for sub-processes execution. One use is a Java application which "manages" several other JVMs created by ProcessBuilders. One of the key requirements is that when a sub-process is stuck, or the hosting application is terminating, it must be able to kill the sub-processes.
The problem is, on one hand, doing this:
new ProcessBuilder("java", "...").start();
Causes this:
Could not find or load main class ...
As if the system variables or directory are different (which they're not). On the other hand, wrapping it in a cmd.exe like this:
new ProcessBuilder("cmd.exe", "/c", "java", "...").start();
WORKS, but creates another cmd.exe process, which has a side effect: the child JVM is now a sub-sub-process, and process.destroy(); doesn't kill it (a known bug in the Windows JRE as I found).
This specific problem was handled on a different level, since all those applications are ours and we know their PIDs. But it's an example how cmd.exe makes everything work differently (or prevent the JVM from working at all). So I'd like to know what exactly happens there.
Here the framework itself comes into the picture as well. It's also going to be used by our testing platform. I'd like to provide an API which allows wrapping the command with a cmd.exe /c by a parameter. But, what exactly is the meaning of that parameter? How do the users decide if they want a cmd.exe wrapping?
AND a bonus I'd appreciate: is any of this relevant in other OS? Does it have some kind of an equivalent, say, in Linux?
Came across it again and found the problem, and some additional insights.
#HarryJohnston - good point, according to the javadoc the sub-process inherits the environment from both ProcessBuilder and Runtime.getRuntime.exec(...) APIs so it's not the issue (and java.exe is in fact found so PATH is obviously available).
But it seems certain command line features are lost. Among others it's this kind of %VARIABLE% usage in command line - they're not interpreted unless the command starts with cmd.exe /c.
In this case of class not found (it could also cause NoClassDefFoundError), I had several variables used in the classpath (perhaps I should have posted the entire command, but it's kinda long). When I replaced them with the literary paths, everything worked.
Basically:
java -cp %classpath% Main - bad
cmd.exe /c java -cp %classpath% Main - good
java -cp D:\proj\bin Main - good
So, on the general question of how's the behavior different with cmd.exe:
Variable references like this: %VARIABLE% are only interpreted by cmd.exe.
With cmd.exe you don't really need to split the command into String array of parameters, you can use one long String (array of "cmd.exe", "/c", "the rest...").
The sub-process will be cmd.exe and not the actual executable, so process.destroy() won't kill it. This might be a bug fixed in later versions (I used Java 7 and 8, Windows 7 and Server 2012).
File associations only work with cmd.exe, so you can't "execute" a file that is not a program or script ("CreateProcess error=193, %1 is not a valid Win32 application"). Calling executables defined in PATH does work.
start and call are only available under cmd.exe.
Command level errors behave differently as well: With cmd.exe the error will just be in the output stream, whereas without it the API throws an exception, which might also have a slightly different description.
For example:
nothing will cause java.io.IOException: CreateProcess error=2, The system cannot find the file specified
cmd.exe /c nothing will return output: 'nothing' is not recognized as an internal or external command, operable program or batch file, and a return value of 1 (the only indication something went wrong).
On the deeper question of why, and is this the full list of differences, I still don't really know. Looks like the JVM has its way of running commands, which you can "wrap" with cmd.exe for additional features and protection.
Another interesting fact is when you run a Batch, it's actually run under a cmd.exe (with all its features). From my tests it seems process.destroy() can only kill it if it's not waiting for anything external (if stuck on pause for example), with or without additional cmd.exe /c wrapping, but not if it runs a sub-process - which is consistent with the cmd.exe limitation mentioned in point 3.
I have a program which allows me to define the java executable (/usr/bin/java), but does not allow me to add specific arguments to the executable.
I want to be able to run Java with a specific argument each time in order to enable Security Manager.
So far, I have tried to add the argument after /usr/bin/java, so it looks like
java=/usr/bin/java -Djava.security.manager -Djava.security.policy=/home/java.policy
That did not work as the program probably checks to see if a file exists. Another way I tried was to make a bash script called java which contained:
/usr/bin/java -Djava.security.manager -Djava.security.policy=/home/java.policy $*
I then set the java path to /home/java (Location of my script). That however did not work either. Is there some sort of way I can do this?
Thanks.
Put your java call in a shell script java.sh:
#!/bin/bash
/usr/bin/java -Djava.security.manager -Djava.security.policy=/home/java.policy $#
Change permissions with chmod u+x java.sh, then call your program with java=./java.sh (adapt path for your script as needed).
Notes about executable bit and shebang line
Both the shebang line (#!/bin/bash) and execute permission are important here. Without them, system calls of the exec* family will fail because the kernel does not know what to do with the file or because execution is rejected due to the missing executable bit.
This is different when run directly from a shell (./java.sh), because most shells have some compatibility feature for that case so they will run a script in a shell if exec* fails. The execute permission must be set though.
The only case where neither is needed is if you give your script as argument to the shell: bash java.sh.
Your second approach would be ok, but your problem is likely to be that your Multicraft application is not finding your script. More so, it's the environment of your web server (Apache?) that may need to have PATH altered to be able to find your java wrapper script.
I have a Java program utility that I want to execute as a command in cmd. I added the location to the PATH variable, but java programs needs to be executed using java - jar "...". How do I shorten that to just the program name, like mysql or netstat?
Update:
I neglected to mention that this java program takes arguments of its own to handle its tasks, so the batch program would need to pass the arguments passed to it over the the java program. I'm not skilled enough in batch to know how to do this.
~Jacob
You could create a batch file or bash script (depending upon your OS) that calls the program with the proper java -jar commands, and simply name the batch (or bash) script whatever you would like to enter as the command. Place this in a directory that is in your PATH variable, and have at it.
Edit: Read this for info on how to parse command line parameters in batch scripts. Just take the parameters passed to the batch file, parse them, and pass them to your jar file with:
java -jar jarfile.jar param1 param2 ...
So for example, lets's assume that your program takes two arguments. Your script could then be as follows:
java -jar jarfile.jar %1 %2
I am not an expert in batch files by any means, so there is probably a more proper way to do this. That being said, why over complicate things?
With Launch4J you can wrap a Java program in a standalone executable file. I'm not going to copy their (long) feature list here, but definite highlights are the numerous ways presented to customize the resulting exe, its small size, the fact that it's open source and its permissive license that allows commercial usage.
The following doesn't work in Java (an exception is thrown):
Runtime.getRuntime().exec("cd mydir; myprog");
The same works fine in PHP:
exec("cd mydir; myprog");
What exactly is different in Java's implementation and why (it seems more limited at first glance)?
the java exec command does not use the system command interpreter. something like "cd mydir; myprog" depends on the system command line interpreter (e.g. on windows cmd, on linux sh) to split that into 2 separate commands and execute each of them. java does not invoke the system command interpreter, so that does not work. you either need to call each command separately, or invoke the desired interpreter yourself as part of the command line.
I've seen people have problems like this, and I'm sure there are several ways, however the one I've seen most people reply is this. add cmd before it.
Runtime.getRuntime().exec("cmd cd mydir; myprog");
Assuming you're running an applet, not Java in a CLI environment on the server? If so, then your Java runtime is running on the client computer, not the server.
Java also has a better way to handle multiple commands than your semicolon. Instead of using the signature:
Runtime.exec(String)
try using this for each of your commands:
Runtime.exec(String[])
and make each argument of your command an element in the String array.
I am running a shell script through a web application. This shell script looks something like
`#! /bin/bash
user=""
pass=""
db_url=""
db_instance=""
sqlplus -s $user/$pass#$db_url/$db_instance # ./SqlScripts/foo.sql
sqlplus -s $user/$pass#$db_url/$db_instance # ./SqlScripts/bar.sql
CLASS_PATH="./lib/*"
java -classpath $CLASS_PATH package.Main ./Data/inputfile`
I am using ProcessBuilder to run the script and everything but the last line works fine. Am I creating a problem by calling shell through the jvm then calling the jvm again to run the application?
The problem was the environment that the script execution process was running in. I changed some of the environment variables of the process and everything is working fine now. The script was initially a standalone shell script, but I wrote one script for each of the databases used. In order to control the workflow I wrote a web application for this which calls seperate threads for each script and can manage the threads. Thanks for the responses!
Often, app servers run their servlets in a 'clean room' environment - e.g. they strip away all the variables that would normally be set from the outside for security reasons. Try using a fully qualified path to the java binary, and also try setting a full/absolute path for your CLASS_PATH variable.
The parent JVM and the child JVM should be separate processes, no particular reason why they should interfere.
What error do you get?
is java on your PATH?
OK, adding more questions in response to your comments ...
Which thread is waiting? Presumably the parent?
The child java process, do you have any evidence as to whether is succesfully initalises. My guess woukld be that the child is in some way blocked. If you kill the child does the parent then come back to life?
Suppose it was a simple "hello world" application, would that work?
Most likely the line:
CLASS_PATH="./lib/*"
And
$CLASS_PATH
It won't be expanded by the process builder because that's usually shells' job, which in this situation is not being invoked.
Try creating the complete list of ./lib/* and append it directly into the last line of your script.
java -classpath ./lib/a.jar:./lib/b.jar
Side note:
Invoking all this from java looks just bad to me. I would rather have it in a standalone script and invoke it by other means, but that's me.