grails: pass program arguments to trait injector via classpath JAR to avoid problems with too long command line (IDEA-158581)

This commit is contained in:
nik
2016-07-26 09:49:55 +03:00
parent 319adf8e1f
commit ed6660ce68
3 changed files with 94 additions and 6 deletions
@@ -49,6 +49,7 @@ public class CommandLineWrapper {
}
private static MainPair loadMainClassFromClasspathJar(File jarFile, String[] args) throws Exception {
String[] mainArgs;
final JarInputStream inputStream = new JarInputStream(new FileInputStream(jarFile));
try {
final Manifest manifest = inputStream.getManifest();
@@ -61,6 +62,14 @@ public class CommandLineWrapper {
System.setProperty(optionName, (String)vmOptions.get(optionName));
}
}
String programParameters = manifest.getMainAttributes().getValue("Program-Parameters");
if (programParameters == null) {
mainArgs = new String[args.length - 2];
System.arraycopy(args, 2, mainArgs, 0, mainArgs.length);
}
else {
mainArgs = splitBySpaces(programParameters);
}
}
finally {
if (inputStream != null) {
@@ -69,11 +78,60 @@ public class CommandLineWrapper {
jarFile.deleteOnExit();
}
String[] mainArgs = new String[args.length - 2];
System.arraycopy(args, 2, mainArgs, 0, mainArgs.length);
return new MainPair(Class.forName(args[1]), mainArgs);
}
/**
* The implementation is copied from copied from com.intellij.util.execution.ParametersListUtil.parse and adapted to old Java versions
*/
private static String[] splitBySpaces(String parameterString) {
parameterString = parameterString.trim();
final ArrayList params = new ArrayList();
final StringBuffer token = new StringBuffer(128);
boolean inQuotes = false;
boolean escapedQuote = false;
boolean nonEmpty = false;
for (int i = 0; i < parameterString.length(); i++) {
final char ch = parameterString.charAt(i);
if (ch == '\"') {
if (!escapedQuote) {
inQuotes = !inQuotes;
nonEmpty = true;
continue;
}
escapedQuote = false;
}
else if (Character.isWhitespace(ch)) {
if (!inQuotes) {
if (token.length() > 0 || nonEmpty) {
params.add(token.toString());
token.setLength(0);
nonEmpty = false;
}
continue;
}
}
else if (ch == '\\') {
if (i < parameterString.length() - 1 && parameterString.charAt(i + 1) == '"') {
escapedQuote = true;
continue;
}
}
token.append(ch);
}
if (token.length() > 0 || nonEmpty) {
params.add(token.toString());
}
//noinspection SSBasedInspection
return (String[])params.toArray(new String[params.size()]);
}
private static class MainPair {
private Class mainClass;
private String[] args;
@@ -18,6 +18,7 @@ package com.intellij.execution.configurations;
import com.intellij.execution.ExecutionException;
import com.intellij.execution.process.OSProcessHandler;
import com.intellij.execution.process.ProcessTerminatedListener;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.projectRoots.JavaSdkType;
import com.intellij.openapi.projectRoots.JdkUtil;
import com.intellij.openapi.projectRoots.Sdk;
@@ -33,6 +34,7 @@ import java.nio.charset.Charset;
* @author Gregory.Shrago
*/
public class SimpleJavaParameters extends SimpleProgramParameters {
private static final Logger LOG = Logger.getInstance(SimpleJavaParameters.class);
private Sdk myJdk;
private String myMainClass;
private final PathsList myClassPath = new PathsList();
@@ -41,6 +43,7 @@ public class SimpleJavaParameters extends SimpleProgramParameters {
private boolean myUseDynamicClasspath;
private boolean myUseClasspathJar = false;
private boolean myUseDynamicVMOptions;
private boolean myPassProgramParametersViaClasspathJar;
private String myJarPath;
public String getMainClass() {
@@ -105,10 +108,27 @@ public class SimpleJavaParameters extends SimpleProgramParameters {
return myUseClasspathJar;
}
/**
* Call this method and pass {@code true} to pass classpath of the application via MANIFEST.MF file in a specially generated classpath.jar
* archive instead of passing it via -classpath command line option. This may be needed to avoid problems with too long command line on Windows.
*/
public void setUseClasspathJar(boolean useClasspathJar) {
myUseClasspathJar = useClasspathJar;
}
public boolean isPassProgramParametersViaClasspathJar() {
return myPassProgramParametersViaClasspathJar;
}
/**
* Call this method and pass {@code true} to pass program parameters via attribute in MANIFEST.MF of the classpath jar instead of passing
* them via command line. This may be needed to avoid problems with too long command line on Windows.
*/
public void setPassProgramParametersViaClasspathJar(boolean passProgramParametersViaClasspathJar) {
LOG.assertTrue(myUseClasspathJar);
myPassProgramParametersViaClasspathJar = passProgramParametersViaClasspathJar;
}
@NotNull
public GeneralCommandLine toCommandLine() {
Sdk jdk = getJdk();
@@ -170,10 +170,12 @@ public class JdkUtil {
commandLine.withParentEnvironmentType(javaParameters.isPassParentEnvs() ? ParentEnvironmentType.CONSOLE : ParentEnvironmentType.NONE);
final Class commandLineWrapper;
boolean passProgramParametersViaClassPathJar = false;
if ((commandLineWrapper = getCommandLineWrapperClass()) != null) {
if (forceDynamicClasspath && !vmParametersList.hasParameter("-classpath") && !vmParametersList.hasParameter("-cp")) {
if (isClassPathJarEnabled(javaParameters, PathUtil.getJarPathForClass(ClassPath.class))) {
appendJarClasspathParams(javaParameters, commandLine, vmParametersList, commandLineWrapper);
passProgramParametersViaClassPathJar = javaParameters.isPassProgramParametersViaClasspathJar();
appendJarClasspathParams(javaParameters, commandLine, vmParametersList, commandLineWrapper, passProgramParametersViaClassPathJar);
}
else {
appendOldCommandLineWrapper(javaParameters, commandLine, vmParametersList, commandLineWrapper);
@@ -197,7 +199,9 @@ public class JdkUtil {
commandLine.addParameter(jarPath);
}
commandLine.addParameters(javaParameters.getProgramParametersList().getList());
if (!passProgramParametersViaClassPathJar) {
commandLine.addParameters(javaParameters.getProgramParametersList().getList());
}
commandLine.withWorkDirectory(javaParameters.getWorkingDirectory());
@@ -283,7 +287,9 @@ public class JdkUtil {
private static void appendJarClasspathParams(SimpleJavaParameters javaParameters,
GeneralCommandLine commandLine,
ParametersList vmParametersList, Class commandLineWrapper) {
ParametersList vmParametersList,
Class commandLineWrapper,
boolean storeProgramParametersInJar) {
try {
final Manifest manifest = new Manifest();
manifest.getMainAttributes().putValue("Created-By",
@@ -305,6 +311,10 @@ public class JdkUtil {
else {
commandLine.addParameters(vmParametersList.getList());
}
if (storeProgramParametersInJar) {
manifest.getMainAttributes().putValue("Program-Parameters", ParametersListUtil.join(javaParameters.getProgramParametersList().getList()));
}
final boolean notEscape = vmParametersList.hasParameter(PROPERTY_DO_NOT_ESCAPE_CLASSPATH_URL);
final List<String> classPathList = javaParameters.getClassPath().getPathList();
@@ -313,7 +323,7 @@ public class JdkUtil {
final String jarFile = classpathJarFile.getAbsolutePath();
commandLine.addParameter("-classpath");
if (writeDynamicVMOptions) {
if (writeDynamicVMOptions || storeProgramParametersInJar) {
commandLine.addParameter(PathUtil.getJarPathForClass(commandLineWrapper) + File.pathSeparator + jarFile);
appendEncoding(javaParameters, commandLine, vmParametersList);
commandLine.addParameter(commandLineWrapper.getName());