package com.waldura.exec; import java.io.*; import java.util.Enumeration; import java.util.StringTokenizer; import java.util.zip.ZipFile; import java.util.zip.ZipEntry; /** * Launches the Java VM in a new process in order to execute a Java class. *

* If the following kind of command is passed to the Exec.getInstance method, * this class will be instantiated in order to execute the specified class file by launching * the Java VM in a new process: *

* com/acme/package/ClassName.class ... *

* The Exec.getInstance method in turn is reused to launch the VM executable, * passing the additional arguments. */ class JavaClassExec extends Exec { /** * Temp buffer used for copying data. */ private static final int BUFFER_SIZE = 64 * 1024; // 64 KB /** * Constructor JavaClassExec. * @param command */ protected JavaClassExec(String[] command) { super(command); } /** * Executes the Java VM executable passing the class to be executed as a parameter. * * @throws IOException Unable to execute the Java VM. */ public void execute() throws IOException { String[] command = getCommand(); if (command == null || command.length == 0 || !command[0].endsWith(".class")) { throw new IllegalArgumentException("JavaClassExec: No class file path specified to be executed."); } String classFilePath = command[0]; // copy the remaining arguments to a new array String[] arguments = new String[command.length - 1]; System.arraycopy( command, 1, // src, src_index arguments, 0, // dest, dest_index arguments.length // length ); logEvent("Class file to execute: " + classFilePath); File jvmExecutable = findJavaVMExecutable(); logEvent("Java VM executable found: " + jvmExecutable); File jarFile = findJarInClassPath(classFilePath); logEvent("Class file found in jar: " + jarFile); jarFile = new File(copyFile(jarFile, cutOutExtension(jarFile.getName()) + "2.jar")); // Converts the class file path to the full class name. String className = cutOutExtension(classFilePath).replace('/', '.'); executeJavaClass(jvmExecutable, jarFile, className, arguments); } /** * Method cutOutExtension. * @todo write this method! * @param string * @return String */ private String cutOutExtension(String string) { throw new UnsupportedOperationException("Not implemented"); } /** * Prints a message about an event in the error output, adding the "JavaClassExec: " prefix. * * @param message The message to be logged. */ private void logEvent(String message) { System.err.println("JavaClassExec: " + message); } /** * Attemps to find the Java VM executable in the Java home directory, as specified by the * "java.home" system property. * * @returns The full path to the Java VM executable. * * @throws IOException Unable to find the Java VM executable. */ private File findJavaVMExecutable() throws IOException { String javaHomePath = getSystemProperty("java.home"); for (int i = 0; i < JVM_EXEC_LIST.length; i++) { File jvmExecutable = new File(javaHomePath, JVM_EXEC_LIST[i]); if (jvmExecutable.exists()) { return jvmExecutable; } } throw new IOException( "JavaClassExec: No Java VM executable found in the Java home directory: " + javaHomePath); } /** * Returns the value of the specified system property. * * @param propertyName The name of the requested system property. * * @throws IOException The property is empty or undefined. */ private String getSystemProperty(String propertyName) throws IOException { String propertyValue = System.getProperty(propertyName); if (propertyValue == null || propertyValue.trim().equals("")) { throw new IOException( "JavaClassExec: System property is empty or undefined: " + propertyName); } return propertyValue; } /** * Attempts to find the jar file that contains the specified class file * in the "java.class.path" system property. * * @param classFilePath The path inside the jar file to the class file. * * @return The jar file containing the class file. * * @throws Unable to find a jar file that contains the the specified class file. */ private File findJarInClassPath(String classFilePath) throws IOException { StringTokenizer tokenizer = new StringTokenizer(getSystemProperty("java.class.path"), File.pathSeparator); while (tokenizer.hasMoreTokens()) { try { File file = new File(tokenizer.nextToken()); if (findClassInJar(classFilePath, file)) { return file; } } catch (IOException e) { // If anything bad happens here, it cannot be the file we're // looking for. So, keep looking. } } throw new IOException( "JavaClassExec: No jar file found in class path that contains the class file: " + classFilePath); } /** * Returns true if the specified class file is found in the specified jar file. * * @param classFilePath The path to the class to be found. * @param file The jar file that might contain the class file. * * @return true if the class file is found in the jar file. * * @throws IOException the file is not a jar file */ private boolean findClassInJar(String classFilePath, File file) throws IOException { ZipFile jarFile = new ZipFile(file); for (Enumeration entries = jarFile.entries(); entries.hasMoreElements(); ) { ZipEntry entry = (ZipEntry) entries.nextElement(); if (entry.getName().equals(classFilePath)) { return true; } } return false; } /** * Executes the specified class file in a new process of the specified Java VM. * * @param jvmExecutable The Java VM executable. * @param jarFile The jar file containing the class file. * @param javaClassName The full name of the Java class to be executed. * @param arguments The arguments to be passed to the Java class. * * @throws IOException Unable to execute the specified class file. */ private void executeJavaClass(File jvmExecutable, File jarFile, String mainClass, String[] arguments) throws IOException { String[] command = new String[4 + arguments.length]; boolean isJView = jvmExecutable.toString().endsWith("jview.exe"); command[0] = jvmExecutable.toString(); command[1] = (isJView) ? "/cp" : "-cp"; command[2] = jarFile.toString(); command[3] = mainClass; for (int i = 0; i < arguments.length; i++) { command[i + 4] = arguments[i]; } Exec exec = Exec.newInstance(command); exec.setWait(getWait()); exec.setVerbose(getVerbose()); exec.execute(); } /** * Copy the same in the same directory to a new name. * * @param origFile the name of the file to copy * @param newFileName the name of the new file * * @return the path to the new file * * @throws IOException the copy failed */ protected String copyFile(File origFile, String newFileName) throws IOException { // determine the full path String parent = origFile.getParent(); String newFilePath = (parent == null) ? newFileName : parent + File.separator + newFileName; System.err.println("UpdateService.copyFile: " + origFile + " -> " + newFilePath); FileInputStream fin = null; FileOutputStream fout = null; try { fin = new FileInputStream(origFile); fout = new FileOutputStream(newFilePath); copyStream(fin, fout, new byte[BUFFER_SIZE]); } finally { if (fin != null) fin.close(); if (fout != null) fout.close(); } return newFilePath; } /** * Copy the input stream to the output stream using the buffer. * * @param in the stream to read data from * @param out the stream to copy data to * * @throws IOException an I/O error occurred */ private void copyStream(InputStream in, OutputStream out, byte buffer[]) throws IOException { int bytes = 0; while ((bytes = in.read(buffer)) > 0) { out.write(buffer, 0, bytes); } } /** * All the possible Java VM executables we know about, referenced from java.home. */ private static final String JVM_EXEC_LIST[] = { // First, the Java VM executables for Windows, preferably in "non-console" mode "..\\System32\\wjview.exe", "..\\wjview.exe", "..\\System32\\jview.exe", "..\\jview.exe", "bin\\jrew.exe", "bin\\jre.exe", "bin\\javaw.exe", "bin\\java.exe", // ... then everyone else. "bin/jre", "bin/java" }; }