Tilde expansion (the leading ~) is a feature of the shell. You are not invoking java through a shell, so that is not happening. Use the System.getProperty("user.home") method to find the user's home directory and build the command using that instead of the tilde.
Tilde expansion (the leading ~) is a feature of the shell. You are not invoking java through a shell, so that is not happening. Use the System.getProperty("user.home") method to find the user's home directory and build the command using that instead of the tilde.
Near dupe Java execute process on linux and Difference between ProcessBuilder and Runtime.exec()
In addition to the correct points about tilde-(non-)expansion, you are passing an entire commandline as one argument to new ProcesssBuilder. Unlike Runtime.exec() which treats a single String as a special case and splits into whitespace-delimited tokens mostly (but not exactly) like typical Unix shells, the ProcessBuilder ctor does not do this. This can be seen in the exception message at the beginning of the traceback you posted. You need separate arguments like:
ProcessBuilder builder = new ProcessBuilder("java", "-jar",
System.getProperty("user.home")+"/Documents.Java/myJar.jar");
// three String's passed to vararg, compiler makes array for you
or possibly (but I don't recommend)
String line = "java -jar " + System.getProperty("user.home")+"/Documents.Java/myJar.jar";
ProcessBuilder builder = new ProcessBuilder( line.split(" ") );
// array of three String's passed directly to vararg
And replace java by a full pathname if the desired java program (or a link to it) isn't found first when searching the PATH in effect for your JVM process.
Executing a Java application in a separate process - Stack Overflow
multithreading - How can I start a 'main' in a new process in Java? - Stack Overflow
runtime - Running a java program from another java program - Stack Overflow
How to create a process in Java - Stack Overflow
I suggest you make HelloWorld2 a top level class. It appears java expects a top level class.
This is the code I tried.
class Main
{
static class Main2
{
public static void main ( String [ ] args )
{
System . out . println ( "YES!!!!!!!!!!!" ) ;
}
}
public static void main ( String [ ] args )
{
System . out . println ( Main2 . class . getCanonicalName ( ) ) ;
System . out . println ( Main2 . class . getName ( ) ) ;
}
}
class Main3
{
public static void main ( String [ ] args )
{
System . out . println ( "YES!!!!!!!!!!!" ) ;
}
}
- getCanonicalName and getName return different names. Which one is right? They are both wrong.
- Main3 works.
I think I see a fix for part of the problem: process.waitFor() prevents control from returning to main() before the subprocess ends.
To figure out why your spawned process isn't starting, I'd recommend printing out all the arguments to the ProcessBuilder constructor and checking that a hand-called JVM called with those arguments succeeds. In particular, you need that class name to be the name of a class having a static void main(String[]).
This is a synthesis of some of the other answers that have been provided. The Java system properties provide enough information to come up with the path to the java command and the classpath in what, I think, is a platform independent way.
public final class JavaProcess {
private JavaProcess() {}
public static int exec(Class klass, List<String> args) throws IOException,
InterruptedException {
String javaHome = System.getProperty("java.home");
String javaBin = javaHome +
File.separator + "bin" +
File.separator + "java";
String classpath = System.getProperty("java.class.path");
String className = klass.getName();
List<String> command = new LinkedList<String>();
command.add(javaBin);
command.add("-cp");
command.add(classpath);
command.add(className);
if (args != null) {
command.addAll(args);
}
ProcessBuilder builder = new ProcessBuilder(command);
Process process = builder.inheritIO().start();
process.waitFor();
return process.exitValue();
}
}
You would run this method like so:
int status = JavaProcess.exec(MyClass.class, args);
I thought it made sense to pass in the actual class rather than the String representation of the name since the class has to be in the classpath anyways for this to work.
Two hints:
System.getProperty("java.home") + "/bin/java" gives you a path to the java executable.
((URLClassLoader) Thread.currentThread().getContextClassLoader()).getURL() helps you to reconstruct the classpath of current application.
Then your EXECUTE.application is just (pseudocode):
Process.exec(javaExecutable, "-classpath", urls.join(":"), CLASS_TO_BE_EXECUTED)
Creating a new "java" process from java is not possible since two processes can't share one JVM. (See this question and the accepted answer).
If you can live with creating a new Thread instead of a Process you can do it with a custom ClassLoader. It is as close you can get to a new process. All static and final fields will be reinitialized!
Also note that the "ServerStart class (for the example below) must be in the class path of the current executing JVM):
public static void main(String args[]) throws Exception {
// start the server
start("ServerStart", "arg1", "arg2");
}
private static void start(final String classToStart, final String... args) {
// start a new thread
new Thread(new Runnable() {
public void run() {
try {
// create the custom class loader
ClassLoader cl = new CustomClassLoader();
// load the class
Class<?> clazz = cl.loadClass(classToStart);
// get the main method
Method main = clazz.getMethod("main", args.getClass());
// and invoke it
main.invoke(null, (Object) args);
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
}
And this is the custom class loader:
private static class CustomClassLoader extends URLClassLoader {
public CustomClassLoader() {
super(new URL[0]);
}
protected java.lang.Class<?> findClass(String name)
throws ClassNotFoundException {
try{
String c = name.replace('.', File.separatorChar) +".class";
URL u = ClassLoader.getSystemResource(c);
String classPath = ((String) u.getFile()).substring(1);
File f = new File(classPath);
FileInputStream fis = new FileInputStream(f);
DataInputStream dis = new DataInputStream(fis);
byte buff[] = new byte[(int) f.length()];
dis.readFully(buff);
dis.close();
return defineClass(name, buff, 0, buff.length, (CodeSource) null);
} catch(Exception e){
throw new ClassNotFoundException(e.getMessage(), e);
}
}
}
Assuming a new thread with a new classloader is not enough (I would vote for this solution though), I understand you need to create a distinct process that invokes a main method in a class without having that declared as "jar main method" in the manifest file -- since you don't have a distinct serverstart.jar anymore.
In this case, you can simply call java -cp $yourClassPath your.package.ServerStart, as you would do for running any java application when you don't have (or don't want to use) the manifest Main-Class.
It is a bit strange but you can run the second program without forking it. Just calling the main method in it. So forget the runtime section and do this:
sam2.main(new String[0]);
Of course this way you must compile sam2 at compile time
Each process needs to be allowed to run and finish. You can use Process#waitFor for this purpose. Equally, you need to consume any output from the process at the same time. waitFor will block so you will need use a Thread to read the input (and if you need to, write output to the process)
Depending on the location of the java/class file, you may also need to specify a starting folder from which the execution of the process can start.
Most of this significantly easier using ProcessBuilder
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
public class CompileAndRun {
public static void main(String[] args) {
new CompileAndRun();
}
public CompileAndRun() {
try {
int result = compile("compileandrun/HelloWorld.java");
System.out.println("javac returned " + result);
result = run("compileandrun.HelloWorld");
} catch (IOException | InterruptedException ex) {
ex.printStackTrace();
}
}
public int run(String clazz) throws IOException, InterruptedException {
ProcessBuilder pb = new ProcessBuilder("java", clazz);
pb.redirectError();
pb.directory(new File("src"));
Process p = pb.start();
InputStreamConsumer consumer = new InputStreamConsumer(p.getInputStream());
consumer.start();
int result = p.waitFor();
consumer.join();
System.out.println(consumer.getOutput());
return result;
}
public int compile(String file) throws IOException, InterruptedException {
ProcessBuilder pb = new ProcessBuilder("javac", file);
pb.redirectError();
pb.directory(new File("src"));
Process p = pb.start();
InputStreamConsumer consumer = new InputStreamConsumer(p.getInputStream());
consumer.start();
int result = p.waitFor();
consumer.join();
System.out.println(consumer.getOutput());
return result;
}
public class InputStreamConsumer extends Thread {
private InputStream is;
private IOException exp;
private StringBuilder output;
public InputStreamConsumer(InputStream is) {
this.is = is;
}
@Override
public void run() {
int in = -1;
output = new StringBuilder(64);
try {
while ((in = is.read()) != -1) {
output.append((char) in);
}
} catch (IOException ex) {
ex.printStackTrace();
exp = ex;
}
}
public StringBuilder getOutput() {
return output;
}
public IOException getException() {
return exp;
}
}
}
Now obviously, you should check the return results of the processes, and may be produce a better mechanism for interacting with the processes, but that's the basic idea...
Maybe java.lang.Process could help here ..
The
ProcessBuilder.start()andRuntime.execmethods create a native process and return an instance of a subclass of Process that can be used to control the process and obtain information about it. The class Process provides methods for performing input from the process, performing output to the process, waiting for the process to complete, checking the exit status of the process, and destroying (killing) the process.
There is only one way to create processes in Java, Runtime.exec() - basically it allows you to start a new JVM just as you would via the command line interface.
I have a jar file that is usually run from a batch file and cannot be run by itself. Is there a way I can run this jar file without editing it from my main jar file.
The secondary file is produced by someone who is not me and I do not have its source code, I am allowed to use it though.
I looked it up and found URLClassLoader which may work but from the batch file that normally runs the jar the command includes memory allocation (Xmx, Xms) Can URLCLassLoader do this?
And also can I get the output from that jar, which is just to a terminal window, to display in my main window and can I pass inputs to it from my main window?
I hope this makes sense. Thanks for any help.
You can use java.home system property to find the current JVM:
String jvm = new java.io.File(new java.io.File(System.getProperty("java.home"),
"bin"),
"java").getAbsolutePath();
and then run it using ProcessBuilder (or Runtime.exec).
Note that for JDK java.home points to the JRE directory included in JDK.
It is not possible, in general.
The recipe provided in @khachik's answer will not necessarily work for a non Sun implementation of Java.
The java executable is not necessarily called
javaand doesn't necessarily live in thebinsubdirectory. Even with Sun Java, on Windows there are two executables;javaandjavaw.The command options for the command that starts a JVM are different for different Java implementations. So the
ProcessBuilderstep may involve non-portable arguments.
While most JVMs have adopted the primary Sun java command options, there are numerous differences. For example:
- IBM J9 uses
j9andj9was the executable names. - BEA / Oracle JRockit has different
-Xand-XXoptions. - Jikes RVM uses
rvmas the executable name, and only supports a subset of Sun'sjavaoptions. - IKVM uses
ikvmas the executable name.
(Note: these are just examples that stand out in a cursory reading of the respective online documentation.)
As on your request I summarize my comments in an answer.
Your first problem was that you tried to invoke java.exe on a Linux or Mac box, which does not stick to the Microsoft convention of including the file type into the name. Therefore Linux and Mac usually use java instead.
The path to the executable can vary also from computer to computer. Therefore the probably most generic way is to locate the java executable using the JAVA_HOME environment variable which can be fetched in Java via System.getenv("JAVA_HOME"); or via System.getProperty("java.home"); Note however that they might not obviously point to the desired directory. Especially some Linux distributions place all binary files inside /bin or /usr/bin and therefore ${JAVA_HOME}/bin/java might not be available. In that case you should create a (hard)link to the executable.
If the path is not set you can set it manually in the session of your console you are executing your application (on Windows set JAVA_HOME=C:\Program Files\Java (or setx on newer Windows versions) on Linux export JAVA_HOME=/opt/java). This can also be done with user-priviledges
It is further strongly recommended to take care of in- and output-streams when dealing with processes. The linked article explains in depth why you should take care of it - not only to catch exceptions thrown by the invoked process.
On invoking a process (especially with the Java executable) you have a couple of options:
You can either invoke the Java process directly (assuming Java is on your PATH) with new ProcessBuilder("java", "-cp", "path/to/your/binaries", "package.ClassName"); or you can invoke the Java executable via a shell - on Linux f.e. you could also use new ProcessBuilder("/bin/bash", "-c", "java -cp path/to/your/binaries package.ClassName"); (though the primer one is obviously preferable).
On loading classes/libraries dynamically you have to use the fully qualified class name. So, if you invoke the Java process in the root-directory of your project and your building-tool generates the class-files inside of ./build/classes and your class Test is located in package testpackage you will end up with the following set: ./build/classes/testpackage/Test.class. To start a new Java process which invokes the main method included in your Test.class you have to use the following command: java -cp ./build/classes testpackage.Test else Java will not be able to find the class definition of Test and execute the main-method.
Missing dependencies have to be added to the classpath (-cp ...) segment of the invoking Java command. F.e: java -cp lib/jar1.jar;lib/jar2.jar;build/classes/* package.ClassName. Multiple archives or directories are separated by a ; and an asterisk * is also possible to include everything within a directory.
One note left: if you ever try to ship your "application" you will need to adapt this code to a more generic version (property file f.e) as this will probably fail with high probability as the path might be totally different.
If I have something forgotten, please let me know.
Did you try to run java.exe from command prompt if it does not work there as well you need to set you java path to the JAVA Installation this can be done by setting variable JAVA_PATH in system variables and this should point to your Jdk bin folder.
If this does not wok then I think you need to provide full path to JAVA.exe as the program is trying to find this file and it is not able to find the file and it gives error
On contrary you can try making threads that would be a better solution because threads use less resources and are more efficient
http://www.rgagnon.com/javadetails/java-0014.html
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.nio.file.Paths;
public class CmdExec {
public static void main(String args[]) {
try {
// enter code here
Process p = Runtime.getRuntime().exec(
Paths.get(System.getenv("windir"), "system32", "tree.com /A").toString()
);
// enter code here
try(BufferedReader input = new BufferedReader(new InputStreamReader(p.getInputStream()))) {
String line;
while ((line = input.readLine()) != null) {
System.out.println(line);
}
}
} catch (Exception err) {
err.printStackTrace();
}
}
}
You can get the local path using System properties or a similar approach.
http://download.oracle.com/javase/tutorial/essential/environment/sysprop.html
The Java Class Library represents external processes using the java.lang.Process class. Processes can be spawned using a java.lang.ProcessBuilder:
Process process = new ProcessBuilder("processname").start();
or the older interface exposed by the overloaded exec methods on the java.lang.Runtime class:
Process process = Runtime.getRuntime().exec("processname");
Both of these will code snippets will spawn a new process, which usually executes asynchronously and can be interacted with through the resulting Process object. If you need to check that the process has finished (or wait for it to finish), don't forget to check that the exit value (exit code) returned by process.exitValue() or process.waitFor() is as expected (0 for most programs), since no exception is thrown if the process exits abnormally.
Also note that additional code is often necessary to handle the process's I/O correctly, as described in the documentation for the Process class (emphasis added):
By default, the created subprocess does not have its own terminal or console. All its standard I/O (i.e. stdin, stdout, stderr) operations will be redirected to the parent process, where they can be accessed via the streams obtained using the methods getOutputStream(), getInputStream(), and getErrorStream(). The parent process uses these streams to feed input to and get output from the subprocess. Because some native platforms only provide limited buffer size for standard input and output streams, failure to promptly write the input stream or read the output stream of the subprocess may cause the subprocess to block, or even deadlock.
One way to make sure that I/O is correctly handled and that the exit value indicates success is to use a library like jproc that deals with the intricacies of capturing stdout and stderr, and offers a simple synchronous interface to run external processes:
ProcResult result = new ProcBuilder("processname").run();
jproc is available via maven central:
<dependency>
<groupId>org.buildobjects</groupId>
<artifactId>jproc</artifactId>
<version>2.5.1</version>
</dependency>
If this is for testing purposes, just launch the other process from the command line or use Eclipse to take care of it. In Eclipse the same project can have multiple main() entry points. When you wish to run the app you create a run / debug configuration that says which entry point you wish to invoke. So you could define one for the client and one for the server and run them with a button click.
Expanded:
Prerequisite - import your project into Eclipse first before doing any of this.
- Run Eclipse from the Java perspective (which is normally the case for a Java project)
- You will see two toolbar buttons marked Debug As... and Run As.... I will describe the Debug button from now on but the same principle applies to Run As..
- Next to the Debug As... button is a drop down button. Click it and from the drop down choose Debug Configurations...
- A configuration dialog will open. In the Dialog double click on "Java Application". Eclipse will create a new debug configuration for debugging a Java application.
- Use the fields on the right to choose which Eclipse project you are debugging, the main class (i.e. the one with the static main you wish to invoke) and any other args you want to set. You can give your configuration a meaningful name and apply the changes.
- Create two configurations one for your client and one for your server, e.g. "debug client" and "debug server"
- Exit the dialog
- Now the Debug As... drop down contains two actions with your new configurations. You can click them to launch your apps in debug mode. You will observe that each instance is running in a separate java.exe process.
Eclipse also has a debug perspective where you can stop / pause running processes, set breakpoints and whatnot. It can even debug more than one thing simultaneously so you could launch both client and server in debug and set breakpoints either side of the call.
Java allows you to make system calls like so.
Runtime r = Runtime.getRuntime();
Process p = r.exec("java otherMain arg0 arg1");
This would allow you to start another java process. Process also has methods to get the output of that process if you need it.
You are using a semicolon as a path separator, while on unix it is a colon.
Use File.pathSeparatorChar instead
The items in a classpath (-cp) under linux have to be seperated by a colon : and under windows by a semicolon ;. And in windows path seperators are backslashes instead of slashes. Build your classpath with File.separator, although it should also work with slashes.