Relaunching the VM

Hi,

Does anyone know a way to relaunch the VM in a system-independent manner?

I’ve got a problem where I need to get away from the WebStart class-loader and its signing crap and the only way I can see it working is if I relaunch a new VM using something like Runtime.exec(). Apart from getting around webstart, this code would be handy for relaunching the VM to use different Java2D pipeline flags 8).

So the code would look something like:

String javaExeName = “java.exe”; // this would only work on windows, anyone know the linux & mac version?
File myJarFile = new File(“C:/myJarFile.jar”);
String fileSeparator = System.getProperty(“file.separator”);

String commandString = System.getProperty(“java.home”) + fileSeparator + “bin” + fileSeparator + javaExeName +" “;
// would the classpath need to be set here? Maybe that would that go like:
// String classPathOption = “-classpath”+” “+”""+myJarFileName.getParent().getAbsolutePath()+""";
// commandString += classPathOption;
commandString += “”"+myJarFile.getAbsolutePath()+""";
// I think the above would work so long as the main-class was specified in the manifest file (which is inside the jar).
Runtime.getRuntime().exec(commandString);

Googling the topic hasn’t been successful for me but I did find this (a system-independent way of launching the browser using Runtime.exec()).

If anyone can help with the ‘javaExeName’ variable for other OS’s or anything else that would be great. :slight_smile:

Thanks,
Keith

PS: I’ve raised this issue before here: http://www.java-gaming.org/forums/index.php?topic=14092.15
Also, the below code actually works. It spawns a new VM (on windows) but can’t be used from Webstart since the classpath location isn’t returned on a webstarted-app.
String fileSeparator = System.getProperty(“file.separator”);
String commandString = System.getProperty(“java.home”)+fileSeparator+“bin”+fileSeparator+“java.exe”+" “;
String classPathOption = “-classpath”+” “+”""+System.getProperty(“java.class.path”)+""";
commandString += classPathOption+" ";
String fullClassName = Main.class.getCanonicalName();
commandString += fullClassName;
Runtime.getRuntime().exec(commandString);

PPS: This is the problem I’m having with webstart: http://www.java-gaming.org/forums/index.php?topic=15397.0

On linux (and possibly OSX) it’s just “java”

same on windows :stuck_out_tongue:

Erm, I don’t think runtime.exec is allowed unless you have a signed webstart app.

Done some digging around, found this article and it seems to confirm it

[quote]In practical terms the sandbox restricts your program from talking to the host environment in any way. Files, network access, and most system functions are off-limits, as are various ways of getting around these restrictions, like using custom classloaders, Runtime.exec() calls, or native libraries.
[/quote]
Might be worth trying, but don’t be surprised if it doesn’t work unless your app is signed. I’d be worried if it does as it means the sandbox can be breached without my permission.

Endolf

Yup, my first thought.

This would never ever be allowed, as it would render all whole security-layer useless.

You simply can’t break out of the sandbox…

maybe setting the reference (pointer) to the security-manager to null (0) with Unsafe.. but i think Reflection (which is required to use Unsafe), is heavily restricted by the security-manager Sun just must have thought about this.

As I understand it from the second linked thread in the original post, his app is signed. He wants to spawn another VM to get around the fact that the classes he is generating at runtime do not inherit the signed-ness of the generating classes, and thus raise a security exception.

We will do something similar in the Xith3D project to be able to override the bootclasspath on Mac Os X. For the time being, Amos has started a little framework to start his digibots school project. You can download the jar and will find the “oneclick” source included: http://mk3d.free.fr/digibots/

Maybe we should join forces and make this framework a generic solution for this kind of webstart and single-jar deployment.

cylab.

Yeah, exactly.

Runtime.exec() doesn’t care about the extension of the executables. I mean, if you give it a “.exe” file, then fine. But if you just give it “java”, on windows it’ll find “java.exe” (and maybe others, java.lnk, java.pif, java.bat, who knows ?) and I guess on mac it’ll find the right file :slight_smile:

At least in OneClick I do just java.home + “java” and it works perfectly on Linux + Windows.

(NOTE : In the current OneClick version, you will find sometimes that Digibots (which is the only example I have, so far) doesn’t launch until you kill the “javaw.exe” process and leave the “java.exe” exe running. This will be fixed soon and is not related to the “java”/“java.exe” issue).

Thanks for the help everyone. About java vs java.exe I didn’t know it would work but yes you’re right, it does! 8)

So this code appears to work for me (on Windows, java1.6):

	String javaExeName = "java";
	File myJarFile = new File("E:/JavaFiles/STG/dist/STG.jar");
	String fileSeparator = System.getProperty("file.separator");
	String jreJavaFileName = System.getProperty("java.home") + fileSeparator + "bin" + fileSeparator + javaExeName;// +" ";
	String jarOption = "-jar";//+" ";
	String classPathOption = "-classpath";
	ArrayList<String> commands = new ArrayList<String>();
	commands.add(jreJavaFileName);
	commands.add(jarOption);
	commands.add(myJarFile.getAbsolutePath());

	System.out.println("About to launch new VM with the following commands:");
	for (String aCommand : commands){
		System.out.print(aCommand);
		if (commands.indexOf(aCommand) != commands.size()-1){
			System.out.print(" ");
		}
	}
	System.out.println();

	try{
		ProcessBuilder processBuilder = new ProcessBuilder(commands);
		Process process = processBuilder.start();
	}catch(IOException ex){
		ex.printStackTrace();
	}

Now my plan is to use WebStart to launch the program, then restart my app from there. Webstart caches the jar in all different inaccessible spots (which are different depending on the VM). I think the best solution to get my jar files on the user’s system is to bundle the jar as a resource in the webstart-downloaded jar file then use:

InputStream in = Main.class.getResourceAsStream(“jarDirectory/myJarFile.jar”);
And then write the bytes to a file on the user’s hard drive, labelling that file myJarFile.jar. That way I get WebStart functionality (like auto-update etc) and my app can start itself without the security-shackled webstart classsloader. The only disadvantage is that I have to wait for 2 VM’s to load before my app starts.

Good idea or do have you a better one??

Thanks,
Keith

Migrate to Slick! ;D

You’re probably not happy with that answer but it solves a lot of webstart issues since it isn’t based on Java 2D. Or you can switch to Java 6 and choose not supporting previous versions of Java, which is not really a good idea if you want to run your game anywhere.

Good news! I think i’ve got it working, test it out here (it launches the BeanShell front-end app I’ve been working on): http://www.freewebs.com/commanderkeith/SlaveBot/SlaveBot.jnlp

I’d really appreciate it if people could try it out and let me know if it works (that is, that it starts at all and that after clicking ‘run’ the program automatically types ‘Hello World’).

I’d love some help improving this relaunching code, feel free to suggest imnprovements to the source. By the way I couldn’t get the Digibot program to work, probably that problem you mentioned.

Yeah the Slick games are really impressive. I just don’t know enough about OGL and all the associated troubles there are with drivers etc.

Thanks,
Keith

PS: 2 problems with the code at the moment are:

  1. The jar files are re-written on every invocation of the program. I do this since I want the jars to be up to date if the webstart jar was re-downloaded. Maybe there’s a more clever way to do it?
  2. The jar files are written to the user.home directory and there’s no fail-over if that directory is off-limits for whatever reason.

To use it, just dump your app’s jars in the same directory as wherever you put this class, then change the relevant parameters in the main method.

package wrapper;

import java.io.*;
import java.util.*;
import java.net.*;

/**
 *
 * @author woodwardk
 */
public class Main {
	
	ArrayList<String> jarList = new ArrayList<String>();
	File jarDirectory;
	
	static final String fileSeparator = System.getProperty("file.separator");
	
	/** Creates a new instance of Main */
	public Main(File jarDirectory, ArrayList<String> jarList) {
		this.jarList = jarList;
		this.jarDirectory = jarDirectory;
	}
	
	public File buildJars() throws IOException{
		/*if (jarDirectory.isDirectory() == false){
			throw new IllegalArgumentException(jarDirectory.getAbsolutePath() + " doesn't exist!");
		}*/
		File mainJarFile = null;
		for (String jarName : jarList){
			// replace any '/' characters with the system's file separator.
			// need to use quoteReplacement or else the windows '\' character behaves wierdly with replaceAll().
			jarName = jarName.replaceAll("/", java.util.regex.Matcher.quoteReplacement(fileSeparator));
			String jarFilePath = jarDirectory.getAbsolutePath()+fileSeparator+jarName;
			File jarFile = new File(jarFilePath);
			System.out.println(jarFilePath);
			// save the file if its not already on the system
			if (jarFile.isFile() == false){
				jarFile.getParentFile().mkdirs();
				jarFile.createNewFile();
			}
			
			// now have to switch the fileSearator back to the java-style '/'    grrr....
			jarName = jarName.replaceAll(java.util.regex.Matcher.quoteReplacement(fileSeparator), "/");
			InputStream in = this.getClass().getResourceAsStream(jarName);
			if (in == null){
				throw new IllegalArgumentException(jarName + " wasn't found!");
			}
			BufferedInputStream bin = new BufferedInputStream(in);
			byte[] bytes = new byte[bin.available()];
			bin.read(bytes);
			bin.close();
			BufferedOutputStream bout = new BufferedOutputStream(new FileOutputStream(jarFile));
			bout.write(bytes);
			bout.flush();
			bout.close();
			if (jarList.indexOf(jarName) == 0){
				mainJarFile = jarFile;
			}
			
		}
		return mainJarFile;
	}
	
	
	public void relaunchVM(File mainJarFile) throws IOException{
		String javaExeName = "java";
		File myJarFile = mainJarFile;
		String jreJavaFilePath = System.getProperty("java.home") + fileSeparator + "bin" + fileSeparator + javaExeName;// +" ";
		String jarOption = "-jar";//+" ";
		//String classPathOption = "-classpath "+myJarFile.getParent() + fileSeparator;	// don't need to specify classpath
		ArrayList<String> commands = new ArrayList<String>();
		commands.add(jreJavaFilePath);
		commands.add(jarOption);
		commands.add(myJarFile.getAbsolutePath());
		
		System.out.println("About to launch new VM with the following commands:");
		for (String aCommand : commands){
			System.out.print(aCommand);
			if (commands.indexOf(aCommand) != commands.size()-1){
				System.out.print(" ");
			}
		}
		System.out.println();
		
		ProcessBuilder processBuilder = new ProcessBuilder(commands);
		Process process = processBuilder.start();
		
	}
	
	/**
	 * @param args the command line arguments
	 */
	public static void main(String[] args) {
		
		ArrayList<String> jarList = new ArrayList<String>();
		jarList.add("SlaveBot.jar");	// the first one should contain the main-class.
		jarList.add("lib/bsh-2.0b4.jar");
		jarList.add("lib/jnlp.jar");
		jarList.add("lib/substance.jar");
		File jarDirectory = new File(System.getProperty("user.home")+"/SlaveBot");
		Main main = new Main(jarDirectory, jarList);
		try{
			File mainJarFile = main.buildJars();
			main.relaunchVM(mainJarFile);
		}catch(IOException e){
			e.printStackTrace();
		}
		System.exit(0);
	}
	
}

it works here… winXP, jre6
Although downloading was extreamly slow and stalled 2 times.

Very nice GUI, how did you make it?

Thanks! :slight_smile: i just made a custom skin using Kirill’s Substance look and feel: https://substance.dev.java.net/

The program download is only 1.8m but freewebs sucks as a host.

If anyone with a mac or linux box would let me know if it works that’d be great.

Thanks,
Keith

The beauty with Slick is that you don’t have to know OGL actually! And the API is really straight forward. About the drivers, I don’t think it’s much an issue now.