319 lines
8.9 KiB
Java
319 lines
8.9 KiB
Java
/*******************************************************************************
|
|
* Copyright (c) 2002 - 2006 IBM Corporation.
|
|
* All rights reserved. This program and the accompanying materials
|
|
* are made available under the terms of the Eclipse Public License v1.0
|
|
* which accompanies this distribution, and is available at
|
|
* http://www.eclipse.org/legal/epl-v10.html
|
|
*
|
|
* Contributors:
|
|
* IBM Corporation - initial API and implementation
|
|
*******************************************************************************/
|
|
package com.ibm.wala.util.processes;
|
|
|
|
import java.io.File;
|
|
import java.io.IOException;
|
|
import java.util.ArrayList;
|
|
import java.util.Collections;
|
|
import java.util.Iterator;
|
|
import java.util.List;
|
|
import java.util.logging.Logger;
|
|
|
|
import com.ibm.wala.util.PlatformUtil;
|
|
|
|
/**
|
|
* A Java process launcher
|
|
*/
|
|
public class JavaLauncher extends Launcher {
|
|
|
|
/**
|
|
* @param programArgs
|
|
* arguments to be passed to the Java program
|
|
* @param mainClass
|
|
* Declaring class of the main() method to run.
|
|
* @param classpathEntries
|
|
* Paths that will be added to the default classpath
|
|
*/
|
|
public static JavaLauncher make(String programArgs, String mainClass, List<String> classpathEntries, Logger logger) {
|
|
return new JavaLauncher(programArgs, mainClass, true, classpathEntries, false, false, logger);
|
|
}
|
|
|
|
/**
|
|
* @param programArgs
|
|
* arguments to be passed to the Java program
|
|
* @param mainClass
|
|
* Declaring class of the main() method to run.
|
|
* @param inheritClasspath
|
|
* Should the spawned process inherit all classpath entries of the
|
|
* currently running process?
|
|
* @param classpathEntries
|
|
* Paths that will be added to the default classpath
|
|
* @param captureOutput
|
|
* should the launcher capture the stdout from the subprocess?
|
|
* @param captureErr
|
|
* should the launcher capture the stderr from the subprocess?
|
|
*/
|
|
public static JavaLauncher make(String programArgs, String mainClass, boolean inheritClasspath, List<String> classpathEntries,
|
|
boolean captureOutput, boolean captureErr, Logger logger) {
|
|
if (mainClass == null) {
|
|
throw new IllegalArgumentException("null mainClass");
|
|
}
|
|
return new JavaLauncher(programArgs, mainClass, inheritClasspath, classpathEntries, captureOutput, captureErr, logger);
|
|
}
|
|
|
|
/**
|
|
* arguments to be passed to the Java program
|
|
*/
|
|
private String programArgs;
|
|
|
|
/**
|
|
* Declaring class of the main() method to run.
|
|
*/
|
|
private final String mainClass;
|
|
|
|
/**
|
|
* Should the spawned process inherit all classpath entries of the currently
|
|
* running process?
|
|
*/
|
|
private final boolean inheritClasspath;
|
|
|
|
/**
|
|
* Should assertions be enabled in the subprocess? default false.
|
|
*/
|
|
private boolean enableAssertions;
|
|
|
|
/**
|
|
* Paths that will be added to the default classpath
|
|
*/
|
|
private final List<String> xtraClasspath = new ArrayList<String>();
|
|
|
|
/**
|
|
* A {@link Thread} which spins and drains stdout of the running process.
|
|
*/
|
|
private Thread stdOutDrain;
|
|
|
|
/**
|
|
* A {@link Thread} which spins and drains stderr of the running process.
|
|
*/
|
|
private Thread stdErrDrain;
|
|
|
|
/**
|
|
* Absolute path of the 'java' executable to use.
|
|
*/
|
|
private String javaExe;
|
|
|
|
/**
|
|
* Extra args to pass to the JVM
|
|
*/
|
|
private List<String> vmArgs = new ArrayList<String>();
|
|
|
|
/**
|
|
* The last process returned by a call to start() on this object.
|
|
*/
|
|
private Process lastProcess;
|
|
|
|
private JavaLauncher(String programArgs, String mainClass, boolean inheritClasspath, List<String> xtraClasspath,
|
|
boolean captureOutput, boolean captureErr, Logger logger) {
|
|
super(captureOutput, captureErr, logger);
|
|
this.programArgs = programArgs;
|
|
this.mainClass = mainClass;
|
|
this.inheritClasspath = inheritClasspath;
|
|
if (xtraClasspath != null) {
|
|
this.xtraClasspath.addAll(xtraClasspath);
|
|
}
|
|
this.javaExe = defaultJavaExe();
|
|
}
|
|
|
|
public String getJavaExe() {
|
|
return javaExe;
|
|
}
|
|
|
|
public void setJavaExe(String javaExe) {
|
|
this.javaExe = javaExe;
|
|
}
|
|
|
|
public void setProgramArgs(String s) {
|
|
this.programArgs = s;
|
|
}
|
|
|
|
public String getProgramArgs() {
|
|
return programArgs;
|
|
}
|
|
|
|
public String getMainClass() {
|
|
return mainClass;
|
|
}
|
|
|
|
public List<String> getXtraClassPath() {
|
|
return xtraClasspath;
|
|
}
|
|
|
|
@Override
|
|
public String toString() {
|
|
StringBuffer result = new StringBuffer(super.toString());
|
|
result.append(" (programArgs: ");
|
|
result.append(programArgs);
|
|
result.append(", mainClass: ");
|
|
result.append(mainClass);
|
|
result.append(", xtraClasspath: ");
|
|
result.append(xtraClasspath);
|
|
result.append(')');
|
|
return result.toString();
|
|
}
|
|
|
|
/**
|
|
* @return the string that identifies the java executable file
|
|
*/
|
|
public static String defaultJavaExe() {
|
|
String java = System.getProperty("java.home") + File.separatorChar + "bin" + File.separatorChar + "java";
|
|
return java;
|
|
}
|
|
|
|
/**
|
|
* Launch the java process.
|
|
*/
|
|
public Process start() throws IllegalArgumentException, IOException {
|
|
String cp = makeClasspath();
|
|
|
|
String heap = "-Xmx800M";
|
|
|
|
// on Mac, need to pass an extra parameter so we can cleanly kill child
|
|
// Java process
|
|
String signalParam = PlatformUtil.onMacOSX() ? "-Xrs" : null;
|
|
|
|
List<String> cmd = new ArrayList<String>();
|
|
|
|
cmd.add(javaExe);
|
|
cmd.add(heap);
|
|
if (signalParam != null) {
|
|
cmd.add(signalParam);
|
|
}
|
|
cmd.add("-classpath");
|
|
cmd.add(cp);
|
|
String libPath = makeLibPath();
|
|
if (libPath != null) {
|
|
cmd.add(libPath);
|
|
}
|
|
if (enableAssertions) {
|
|
cmd.add("-ea");
|
|
}
|
|
if (vmArgs != null) {
|
|
for (String s : vmArgs) {
|
|
cmd.add(s);
|
|
}
|
|
}
|
|
cmd.add(getMainClass());
|
|
if (getProgramArgs() != null) {
|
|
String[] pa = getProgramArgs().split(" ");
|
|
for (String s : pa) {
|
|
if (s.length() > 0) {
|
|
cmd.add(s);
|
|
}
|
|
}
|
|
}
|
|
|
|
String[] cmds = new String[cmd.size()];
|
|
cmd.toArray(cmds);
|
|
|
|
Process p = spawnProcess(cmds);
|
|
stdErrDrain = isCaptureErr() ? captureStdErr(p) : drainStdErr(p);
|
|
stdOutDrain = isCaptureOutput() ? captureStdOut(p) : drainStdOut(p);
|
|
lastProcess = p;
|
|
return p;
|
|
}
|
|
|
|
public Process getLastProcess() {
|
|
return lastProcess;
|
|
}
|
|
|
|
private String makeLibPath() {
|
|
String libPath = System.getProperty("java.library.path");
|
|
if (libPath == null) {
|
|
return null;
|
|
} else {
|
|
return "-Djava.library.path=" + quoteStringIfNeeded(libPath);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Wait for the spawned process to terminate.
|
|
*
|
|
* @throws IllegalStateException
|
|
* if the process has not been started
|
|
*/
|
|
public void join() {
|
|
if (stdOutDrain == null || stdErrDrain == null) {
|
|
throw new IllegalStateException("process not started. illegal to join()");
|
|
}
|
|
try {
|
|
stdOutDrain.join();
|
|
stdErrDrain.join();
|
|
} catch (InterruptedException e) {
|
|
e.printStackTrace();
|
|
throw new InternalError("Internal error in JavaLauncher.join()");
|
|
}
|
|
if (isCaptureErr()) {
|
|
Drainer d = (Drainer) stdErrDrain;
|
|
setStdErr(d.getCapture().toByteArray());
|
|
}
|
|
if (isCaptureOutput()) {
|
|
Drainer d = (Drainer) stdOutDrain;
|
|
setStdOut(d.getCapture().toByteArray());
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Compute the classpath for the spawned process
|
|
*/
|
|
private String makeClasspath() {
|
|
String cp = inheritClasspath ? System.getProperty("java.class.path") : "";
|
|
if (getXtraClassPath() == null || getXtraClassPath().isEmpty()) {
|
|
return quoteStringIfNeeded(cp);
|
|
} else {
|
|
for (Iterator<String> it = getXtraClassPath().iterator(); it.hasNext();) {
|
|
cp += File.pathSeparatorChar;
|
|
cp += (String) it.next();
|
|
}
|
|
return quoteStringIfNeeded(cp);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* If the input string contains a space, quote it (for use as a classpath).
|
|
* TODO: Figure out how to make a Mac happy with quotes. Trailing separators
|
|
* are unsafe, so we have to escape the last backslash (if present and
|
|
* unescaped), so it doesn't escape the closing quote.
|
|
*/
|
|
public static String quoteStringIfNeeded(String s) {
|
|
s = s.trim();
|
|
// s = s.replaceAll(" ", "\\\\ ");
|
|
return s;
|
|
// Check if there's a space. If not, skip quoting to make Macs happy.
|
|
// TODO: Add the check for an escaped space.
|
|
// if (s.indexOf(' ') == -1) {
|
|
// return s;
|
|
// }
|
|
// if (s.charAt(s.length() - 1) == '\\' && s.charAt(s.length() - 2) != '\\')
|
|
// {
|
|
// s += '\\'; // Escape the last backslash, so it doesn't escape the quote.
|
|
// }
|
|
// return '\"' + s + '\"';
|
|
}
|
|
|
|
public boolean isEnableAssertions() {
|
|
return enableAssertions;
|
|
}
|
|
|
|
public void setEnableAssertions(boolean enableAssertions) {
|
|
this.enableAssertions = enableAssertions;
|
|
}
|
|
|
|
public void addVmArg(String arg) {
|
|
this.vmArgs.add(arg);
|
|
}
|
|
|
|
public List<String> getVmArgs() {
|
|
return Collections.unmodifiableList(vmArgs);
|
|
}
|
|
|
|
}
|