I need a clean way to start many instances of a Java program with a GUI, and I want to do it programmatically. The "program" i want to run is just a .class file (a compiled .java file with a main method), it should show a GUI and run independently from the others (as its own process). I also need to pass that program some arguments.
Check EDIT5 for the complete working solution code.
Here's the class that's supposed to start the many processes
package startothermain;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
public class Starter {
public static void main(String[] args) {
int starts = 4;
for (int i = 0; i < starts; ++i) {
System.out.println("Starting an app");
ProcessBuilder pb = new ProcessBuilder("java.exe", "-cp", "bin", "Started", "arg0");
try {
pb.start();
} catch (IOException ex) {
Logger.getLogger(Starter.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
}
This is the class that's supposed to be started and display a GUI
package startothermain;
import javax.swing.JOptionPane;
public class Started {
public static void main(String[] args) {
JOptionPane.showMessageDialog(null, args[0]);
}
}
I tried both ProcessBuilder and Runtime.getRuntime() suggestions I found in other answers, but they don't seem to work. I always get some kind of "not found" error, like this one
SEVERE: null
java.io.IOException: Cannot run program "java.exe": error=2, No such file or directory
at java.lang.ProcessBuilder.start(ProcessBuilder.java:1041)
at startothermain.Starter.main(Starter.java:35)
Caused by: java.io.IOException: error=2, No such file or directory
at java.lang.UNIXProcess.forkAndExec(Native Method)
at java.lang.UNIXProcess.<init>(UNIXProcess.java:135)
I'm working from NetBeans and clicking the Run button, so the .java files should be compiled properly and in the same folder. I hope this is no side problem with the src/build folders created by the IDE.
EDIT: I was trying to find the java.exe, but I'm on Linux. Sigh. I changed it for "java", but I still have the same problem. The path variable should be set. Moreover, I'm worried that, if I give it a full path, it will become unportable.
Trying to get the JAVA_HOME on Linux
System.getenv("JAVA_HOME"); // returns null
System.getProperty("java.home"); // returns /usr/lib/jvm/java-7-openjdk-amd64/jre
And on Windows
System.getenv("JAVA_HOME"); // returns C:\Program Files\Java\jdk1.7.0_51
System.getProperty("java.home"); // returns C:\Program Files\Java\jdk1.7.0_51\jre
EDIT2: This new code does NOT spawn errors, but does not open any GUI either. I tried this on both on Windows and Linux, with the same results.
String javaHome = System.getProperty("java.home");
ProcessBuilder pb = new ProcessBuilder(javaHome + "/bin/java", "-cp", "bin", "Started", "arg0");
EDIT3: I redirected the error and output streams of the processbuilder (not of the crerated processes) and it turns out that the Java ClassLoader can't find the class. I guess the problem is not JAVA_HOME, but rather a path problem.
Here's the new code
package startothermain;
import java.io.File;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
public class Starter {
public static void main(String[] args) {
int starts = 4;
System.out.println(System.getProperty("java.home")); // prints C:\Program Files\Java\jdk1.7.0_51\jre
System.out.println(System.getenv("JAVA_HOME")); // prints C:\Program Files\Java\jdk1.7.0_51
System.out.println("Working Directory = "
+ System.getProperty("user.dir")); // prints C:\Users\Agostino\Documents\NetBeansProjects\StartOtherMain
for (int i = 0; i < starts; ++i) {
System.out.println("Starting an app");
ProcessBuilder pb = new ProcessBuilder("java", "build.classes.startothermain.Started");
File log = new File("log");
pb.redirectOutput(log);
pb.redirectError(log);
try {
pb.start();
} catch (IOException ex) {
Logger.getLogger(Starter.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
}
In the log file I find this error
java.lang.NoClassDefFoundError: build/classes/startothermain/Started (wrong name: startothermain/Started)
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(ClassLoader.java:800)
at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
at java.net.URLClassLoader.defineClass(URLClassLoader.java:449)
at java.net.URLClassLoader.access$100(URLClassLoader.java:71)
at java.net.URLClassLoader$1.run(URLClassLoader.java:361)
at java.net.URLClassLoader$1.run(URLClassLoader.java:355)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:354)
at java.lang.ClassLoader.loadClass(ClassLoader.java:425)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
at java.lang.ClassLoader.loadClass(ClassLoader.java:358)
at sun.launcher.LauncherHelper.checkAndLoadMain(LauncherHelper.java:482)
Exception in thread "main"
EDIT4: now the code works, but if I try to use a .jar library in the Started class, it can't be found.
See this Started class that uses a .jar lib (JavaTuples) that is added to the project libraries through NetBeans
package startothermain;
import java.io.File;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
public class Starter {
public static void main(String[] args) {
int starts = 1;
for (int i = 0; i < starts; ++i) {
System.out.println("Starting an app");
ProcessBuilder pb = new ProcessBuilder();
String fullClassName = Started.class.getName();
pb.command("java", "-cp", "./build/classes", fullClassName, "myArg");
File log = new File("log");
pb.redirectOutput(log);
pb.redirectError(log);
try {
pb.start();
} catch (IOException ex) {
Logger.getLogger(Starter.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
}
Fixed Starter class
package startothermain;
import java.io.File;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
public class Starter {
public static void main(String[] args) {
int starts = 1;
for (int i = 0; i < starts; ++i) {
System.out.println("Starting an app");
ProcessBuilder pb = new ProcessBuilder();
String fullClassName = Started.class.getName();
pb.command("java", "-cp", "./build/classes", fullClassName, "myArg");
File log = new File("log");
pb.redirectOutput(log);
pb.redirectError(log);
try {
pb.start();
} catch (IOException ex) {
Logger.getLogger(Starter.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
}
The error in the ProcessBuilder log
Exception in thread "main" java.lang.NoClassDefFoundError: org/javatuples/Pair
at startothermain.Started.main(Started.java:28)
Caused by: java.lang.ClassNotFoundException: org.javatuples.Pair
at java.net.URLClassLoader$1.run(URLClassLoader.java:366)
at java.net.URLClassLoader$1.run(URLClassLoader.java:355)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:354)
at java.lang.ClassLoader.loadClass(ClassLoader.java:425)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
at java.lang.ClassLoader.loadClass(ClassLoader.java:358)
... 1 more
EDIT5: Working code
Starter class
package startothermain;
import java.io.File;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
public class Starter {
public static void main(String[] args) {
int starts = 1;
for (int i = 0; i < starts; ++i) {
System.out.println("Starting an app");
ProcessBuilder pb = new ProcessBuilder();
String fullClassName = Started.class.getName();
String pathToClassFiles = new File("./build/classes").getPath();
String pathSeparator = File.pathSeparator; // ":" on Linux, ";" on Windows
String pathToLib = new File("./lib/javatuples-1.2.jar").getPath();
pb.command("java", "-cp", pathToLib + pathSeparator + pathToClassFiles, fullClassName, "myArg");
File log = new File("log" + i + ".txt"); //debug log for started process
try {
pb.redirectOutput(log);
pb.redirectError(log);
pb.start();
} catch (IOException ex) {
Logger.getLogger(Starter.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
}
Started class
package startothermain;
import javax.swing.JOptionPane;
import org.javatuples.Pair;
public class Started {
public static void main(String[] args) {
Pair<String, Integer> pair = Pair.with("One", 1);
JOptionPane.showMessageDialog(null, args[0] + " " + pair);
}
}