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"
};
}