/******************************************************************************* * Copyright (c) 2012 Pivotal Software, Inc. * 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: * Pivotal Software, Inc. - initial API and implementation *******************************************************************************/ package org.grails.ide.eclipse.core.launch; import static org.eclipse.debug.core.DebugPlugin.ATTR_PROCESS_FACTORY_ID; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.Assert; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.Platform; import org.eclipse.debug.core.ILaunchConfiguration; import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy; import org.eclipse.debug.core.ILaunchManager; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.launching.IJavaLaunchConfigurationConstants; import org.eclipse.jdt.launching.IRuntimeClasspathEntry; import org.eclipse.jdt.launching.IVMInstall; import org.eclipse.jdt.launching.JavaRuntime; import org.eclipse.osgi.service.environment.Constants; import org.grails.ide.eclipse.core.GrailsCoreActivator; import org.grails.ide.eclipse.core.internal.model.DefaultGrailsInstall; import org.grails.ide.eclipse.core.model.GrailsInstallManager; import org.grails.ide.eclipse.core.model.GrailsVersion; import org.grails.ide.eclipse.core.model.IGrailsInstall; import org.grails.ide.eclipse.core.util.CommandLineUtil; /** * Utility class that deals with command line arguments and Launch configurations and their attributes. * * @author Christian Dupuis * @author Kris De Volder * @since 2.2.0 */ public class GrailsLaunchArgumentUtils { //TODO: KDV: (cleanup) none of the constants below should not need to be public. We should encapsulate all code dealing with // them into nice utility methods to access/compute properties from a Grails LaunchConfiguration. No code outside // of this class should need to handle these attributes directly. private static final String GRAILS_INSTALL_LAUNCH_ATTR = GrailsCoreActivator.PLUGIN_ID+".GRAILS_INSTALL"; public static final String PROJECT_DIR_LAUNCH_ATTR = GrailsCoreActivator.PLUGIN_ID + ".PROJECT_DIR"; /** * Optional attribute: can be set to the name of a class that implements {@link GrailsBuildListener}. * The class must be in the org.grails.ide.eclipse.core plugin, which will be added to the classpath * of the launch if this attribute is set. */ private static final String GRAILS_BUILD_LISTENER = GrailsCoreActivator.PLUGIN_ID + ".GRAILS_BUILD_LISTENER"; /** * Launch configuration attribute to set the location of the ".grails" folder. * See also {@link IGrailsInstall} or {@link DefaultGrailsInstall} method getGrailsMetadataHome(). */ public static final String GRAILS_WORK_DIR_LAUNCH_ATTR = GrailsCoreActivator.PLUGIN_ID + ".GRAILS_WORK_DIR"; /** * Launch configuration attribute with extra system properties (should be retrievable by System.getProperty from * the external process). */ private static String SYSTEM_PROPERTIES_LAUNCH_ATTR = GrailsCoreActivator.PLUGIN_ID + ".SYSTEM_PROPS"; public static String getGrailsBuildListener(ILaunchConfiguration conf) { try { return conf.getAttribute(GRAILS_BUILD_LISTENER, (String)null); } catch (CoreException e) { GrailsCoreActivator.log(e); return null; } } public static void setGrailsBuildListener(ILaunchConfigurationWorkingCopy conf, String className) { // STS-1262, cannot load any grails classes in STS // try { // Assert.isLegal(GrailsBuildListener.class.isAssignableFrom(Class.forName(className)), // "Grails build listener does not implement "+GrailsBuildListener.class.getName()); // } catch (ClassNotFoundException e) { // GrailsCoreActivator.log(e); // } conf.setAttribute(GRAILS_BUILD_LISTENER, className); } public static String mergeArguments(String originalArg, String[] vmArgs, String[] excludeArgs, boolean keepActionLast) { if (vmArgs == null) { return originalArg; } if (originalArg == null) { originalArg = ""; } // replace and null out all vmargs that already exist int size = vmArgs.length; for (int i = 0; i < size; i++) { int ind = vmArgs[i].indexOf(" "); int ind2 = vmArgs[i].indexOf("="); if (ind >= 0 && (ind2 == -1 || ind < ind2)) { // -a bc style int index = originalArg .indexOf(vmArgs[i].substring(0, ind + 1)); if (index == 0 || (index > 0 && originalArg.charAt(index - 1) == ' ')) { // replace String s = originalArg.substring(0, index); int index2 = getNextToken(originalArg, index + ind + 1); if (index2 >= 0) { originalArg = s + vmArgs[i] + originalArg.substring(index2); } else { originalArg = s + vmArgs[i]; } vmArgs[i] = null; } } else if (ind2 >= 0) { // a=b style int index = originalArg.indexOf(vmArgs[i] .substring(0, ind2 + 1)); if (index == 0 || (index > 0 && originalArg.charAt(index - 1) == ' ')) { // replace String s = originalArg.substring(0, index); int index2 = getNextToken(originalArg, index); if (index2 >= 0) { originalArg = s + vmArgs[i] + originalArg.substring(index2); } else { originalArg = s + vmArgs[i]; } vmArgs[i] = null; } } else { // abc style int index = originalArg.indexOf(vmArgs[i]); if (index == 0 || (index > 0 && originalArg.charAt(index - 1) == ' ')) { // replace String s = originalArg.substring(0, index); int index2 = getNextToken(originalArg, index); if (!keepActionLast || i < (size - 1)) { if (index2 >= 0) { originalArg = s + vmArgs[i] + originalArg.substring(index2); } else { originalArg = s + vmArgs[i]; } vmArgs[i] = null; } else { // The last VM argument needs to remain last, // remove original arg and append the vmArg later if (index2 >= 0) { originalArg = s + originalArg.substring(index2); } else { originalArg = s; } } } } } // remove excluded arguments if (excludeArgs != null && excludeArgs.length > 0) { for (int i = 0; i < excludeArgs.length; i++) { int ind = excludeArgs[i].indexOf(" "); int ind2 = excludeArgs[i].indexOf("="); if (ind >= 0 && (ind2 == -1 || ind < ind2)) { // -a bc style int index = originalArg.indexOf(excludeArgs[i].substring(0, ind + 1)); if (index == 0 || (index > 0 && originalArg.charAt(index - 1) == ' ')) { // remove String s = originalArg.substring(0, index); int index2 = getNextToken(originalArg, index + ind + 1); if (index2 >= 0) { // If remainder will become the first argument, // remove leading blanks while (index2 < originalArg.length() && originalArg.charAt(index2) == ' ') { index2 += 1; } originalArg = s + originalArg.substring(index2); } else { originalArg = s; } } } else if (ind2 >= 0) { // a=b style int index = originalArg.indexOf(excludeArgs[i].substring(0, ind2 + 1)); if (index == 0 || (index > 0 && originalArg.charAt(index - 1) == ' ')) { // remove String s = originalArg.substring(0, index); int index2 = getNextToken(originalArg, index); if (index2 >= 0) { // If remainder will become the first argument, // remove leading blanks while (index2 < originalArg.length() && originalArg.charAt(index2) == ' ') { index2 += 1; } originalArg = s + originalArg.substring(index2); } else { originalArg = s; } } } else { // abc style int index = originalArg.indexOf(excludeArgs[i]); if (index == 0 || (index > 0 && originalArg.charAt(index - 1) == ' ')) { // remove String s = originalArg.substring(0, index); int index2 = getNextToken(originalArg, index); if (index2 >= 0) { // Remove leading blanks while (index2 < originalArg.length() && originalArg.charAt(index2) == ' ') { index2 += 1; } originalArg = s + originalArg.substring(index2); } else { originalArg = s; } } } } } // add remaining vmargs to the end for (int i = 0; i < size; i++) { if (vmArgs[i] != null) { if (originalArg.length() > 0 && !originalArg.endsWith(" ")) { originalArg += " "; } originalArg += vmArgs[i]; } } return originalArg; } public static void mergeClasspath(List<IRuntimeClasspathEntry> cp, IRuntimeClasspathEntry entry) { Iterator<IRuntimeClasspathEntry> iterator = cp.iterator(); while (iterator.hasNext()) { IRuntimeClasspathEntry entry2 = iterator.next(); if (entry2.getPath().equals(entry.getPath())) { return; } } cp.add(entry); } public static void replaceJREContainer(List<IRuntimeClasspathEntry> cp, IRuntimeClasspathEntry entry) { int size = cp.size(); for (int i = 0; i < size; i++) { IRuntimeClasspathEntry entry2 = cp.get(i); if (entry2.getPath().uptoSegment(2).isPrefixOf(entry.getPath())) { cp.set(i, entry); return; } } cp.add(0, entry); } protected static int getNextToken(String s, int start) { int i = start; int length = s.length(); char lookFor = ' '; while (i < length) { char c = s.charAt(i); if (lookFor == c) { if (lookFor == '"') { return i + 1; } return i; } if (c == '"') { lookFor = '"'; } i++; } return -1; } /** * Add default -Xmx and -XX:MaxPermSize arguments. These arguments will only be added if they aren't already * present. * * Note: Grails 2.3 uses these as default: * -server -Xmx768M -Xms64M -XX:PermSize=32m -XX:MaxPermSize=256m */ public static List<String> addMemorySettings(List<String> args) { boolean mxFound = false; boolean permSizeFound = false; for (String arg : args) { if (arg.startsWith("-Xmx")) { mxFound = true; } else if (arg.startsWith("-XX:MaxPermSize=") || arg.startsWith("-XX:PermSize=")) { permSizeFound = true; } } List<String> newArgs = new ArrayList<String>(args); if (!permSizeFound) { newArgs.add(0, "-XX:MaxPermSize=256m"); } if (!mxFound) { newArgs.add(0, "-Xmx768M"); } // Add in -server if (!newArgs.contains("-server") && !newArgs.contains("-client")) { newArgs.add(0, "-server"); } return newArgs; } public static List<String> addSpringLoadedArgs(ILaunchConfiguration conf, List<String> vmArgs) throws CoreException { String command = GrailsLaunchConfigurationDelegate.getScript(conf); // STS-2638 include reloading agent when launching in interactive mode // only way to launch in interactive mode is to run with an empty command if (command == null || command.contains("-noreloading")) { return vmArgs; } if (command.length() == 0 || command.contains("run-app") || command.contains("interactive")) { IGrailsInstall install = GrailsLaunchArgumentUtils.getGrailsInstall(conf); if (install != null && install.getVersion().compareTo(GrailsVersion.V_2_0_0) >=0) { File loadedJar = install.getSpringLoadedJar(); File cacheDir = install.getSpringLoadedCacheDir(); ArrayList<String> newArgs = new ArrayList<String>(vmArgs); newArgs.add("-javaagent:"+loadedJar); newArgs.add("-noverify"); newArgs.add("-Dspringloaded=profile=grails;cacheDir="+cacheDir); return newArgs; } } return vmArgs; } @SuppressWarnings({ "deprecation", "unchecked" }) public static void prepareClasspath( ILaunchConfigurationWorkingCopy configuration, IGrailsInstall install, IProject project, IVMInstall vmInstall) throws CoreException { // FIXKDV FIXADE Copies of this code exist in // GrailsCommandLaunchConfigurationDelegate.launch() // and GrailsLaunchConfigurationDelegate.launch() // consider refactoring to combine IJavaProject javaProject = null; if (project!=null && project.hasNature(JavaCore.NATURE_ID)) { javaProject = JavaCore.create(project); } List<IRuntimeClasspathEntry> cp = GrailsLaunchArgumentUtils .getBootstrapClasspath(install); IVMInstall vm = null; if (vmInstall == null) { if (javaProject != null) { vm = JavaRuntime.getVMInstall(javaProject); } else { vm = JavaRuntime.getDefaultVMInstall(); } } else { vm = vmInstall; } if (vm != null) { if (vm!=JavaRuntime.getDefaultVMInstall()) { configuration.setAttribute( IJavaLaunchConfigurationConstants.ATTR_VM_INSTALL_NAME, vm.getName()); configuration.setAttribute( IJavaLaunchConfigurationConstants.ATTR_VM_INSTALL_TYPE, vm .getVMInstallType().getId()); } Map<String, String> envs = configuration.getAttribute( ILaunchManager.ATTR_ENVIRONMENT_VARIABLES, new HashMap<String, String>()); // This should probably not be set here, but only just before the launch. This to ensure using // current project settings to determine JAVA_HOME. Setting this here will persist the variable if configuration is saved // and it will become outdated when project settings are changed. // envs.put("JAVA_HOME", vm.getInstallLocation().getAbsolutePath()); String typeId = vm.getVMInstallType().getId(); replaceJREContainer(cp, JavaRuntime.newRuntimeContainerClasspathEntry( new Path(JavaRuntime.JRE_CONTAINER).append(typeId) .append(vm.getName()), IRuntimeClasspathEntry.BOOTSTRAP_CLASSES)); IPath jrePath = new Path(vm.getInstallLocation().getAbsolutePath()); if (jrePath != null) { IPath toolsPath = jrePath.append("lib").append("tools.jar"); if (toolsPath.toFile().exists()) { IRuntimeClasspathEntry toolsJar = JavaRuntime .newArchiveRuntimeClasspathEntry(toolsPath); int toolsIndex; for (toolsIndex = 0; toolsIndex < cp.size(); toolsIndex++) { IRuntimeClasspathEntry entry = cp.get(toolsIndex); if (entry.getType() == IRuntimeClasspathEntry.ARCHIVE && entry.getPath().lastSegment() .equals("tools.jar")) { break; } } if (toolsIndex < cp.size()) { cp.set(toolsIndex, toolsJar); } else { mergeClasspath(cp, toolsJar); } } } } Iterator<IRuntimeClasspathEntry> iterator = cp.iterator(); List<String> list = new ArrayList<String>(); while (iterator.hasNext()) { IRuntimeClasspathEntry entry = iterator.next(); try { list.add(entry.getMemento()); } catch (Exception e) { } } configuration.setAttribute( IJavaLaunchConfigurationConstants.ATTR_CLASSPATH, list); configuration .setAttribute( IJavaLaunchConfigurationConstants.ATTR_DEFAULT_CLASSPATH, false); } public static List<IRuntimeClasspathEntry> getBootstrapClasspath( IGrailsInstall install) { List<IRuntimeClasspathEntry> cp = new ArrayList<IRuntimeClasspathEntry>(); for (File file : install.getBootstrapClasspath()) { try { cp.add(JavaRuntime.newArchiveRuntimeClasspathEntry(new Path( file.getCanonicalPath()))); } catch (IOException e) { } } return cp; } public static void prepareLaunchConfiguration(IProject project, String script, IGrailsInstall install, String baseDir, ILaunchConfigurationWorkingCopy wc) throws CoreException { wc.setAttribute(IJavaLaunchConfigurationConstants.ATTR_ALLOW_TERMINATE, true); wc.setAttribute(ATTR_PROCESS_FACTORY_ID, GrailsRuntimeProcessFactory.ID); if (project==null) { //Only add this explicitly to the config if outside of a project. //When inside a project we should just follow project settings instead (rather than //persistently store a specific install in the launch config, since it will become //"out of synch" if the user changes project settings). GrailsLaunchArgumentUtils.setGrailsInstall(wc, install); } wc.setAttribute(IJavaLaunchConfigurationConstants.ATTR_MAIN_TYPE_NAME, "org.codehaus.groovy.grails.cli.support.GrailsStarter"); if (project != null) { wc.setAttribute( IJavaLaunchConfigurationConstants.ATTR_PROJECT_NAME, project.getName()); } GrailsLaunchConfigurationDelegate.setScript(wc, script); wc.setAttribute(GrailsLaunchArgumentUtils.PROJECT_DIR_LAUNCH_ATTR, baseDir); // is this correct? Should it be // if (install.getGrailsWorkDir() != null) { // wc.setAttribute(GrailsCoreActivator.ATTRIBUTE_GRAILS_WORK_DIR, // install.getGrailsWorkDir()); // } if (DefaultGrailsInstall.getDefaultGrailsWorkDir() != null) { wc.setAttribute(GrailsLaunchArgumentUtils.GRAILS_WORK_DIR_LAUNCH_ATTR, DefaultGrailsInstall.getDefaultGrailsWorkDir()); } // Need to put something that's non-null or problems picking up native env params! Map<String, String> env = wc.getAttribute(ILaunchManager.ATTR_ENVIRONMENT_VARIABLES, (Map<String, String>)null); if (env==null) { env = new HashMap<String, String>(); } //Map needs to be non-empty so it actually gets persisted. //We will ensure a GRAILS_STS_RUNNING env var is always set. But if it is already set, we skip all of this //to avoid https://issuetracker.springsource.com/browse/STS-2372 String stsRunning = env.get("GRAILS_STS_RUNNING"); if (stsRunning==null) { env.put("GRAILS_STS_RUNNING", "true"); wc.setAttribute(ILaunchManager.ATTR_ENVIRONMENT_VARIABLES, env); wc.setAttribute(ILaunchManager.ATTR_APPEND_ENVIRONMENT_VARIABLES, true); } prepareClasspath(wc, install, project, null); } /** * Prepare a LaunchConfiguration to be able to execute classes in the Grails * install. This method assumes that the LaunchConfiguration is associated * with a Grails project, so we can get the details of the associated grails * install and use that to setup the classpath and environment parameters. * * @throws CoreException */ public static void prepareLaunchConfigurationWithProject( ILaunchConfigurationWorkingCopy wc) throws CoreException { IProject project = getProject(wc); Assert.isLegal(project != null, "No project associated with this launch config"); String baseDir = project.getLocation().toString(); IGrailsInstall install = GrailsCoreActivator.getDefault() .getInstallManager().getGrailsInstall(project); prepareLaunchConfiguration(project, "", install, baseDir, wc); } public static IJavaProject getJavaProject(ILaunchConfiguration wc) throws CoreException { IProject proj = getProject(wc); if (proj != null) return JavaCore.create(proj); return null; } /** * Returns the Java project specified by the given launch configuration, or * <code>null</code> if none. * * @param configuration * launch configuration * @return the Java project specified by the given launch configuration, or * <code>null</code> if none * @exception CoreException * if unable to retrieve the attribute */ public static IProject getProject(ILaunchConfiguration configuration) { try { String projectName = getJavaProjectName(configuration); if (projectName != null) { projectName = projectName.trim(); if (projectName.length() > 0) { return ResourcesPlugin.getWorkspace().getRoot().getProject(projectName); } } } catch (CoreException e) { GrailsCoreActivator.log(e); } return null; } /** * Returns the Java project name specified by the given launch * configuration, or <code>null</code> if none. * * @param configuration * launch configuration * @return the Java project name specified by the given launch * configuration, or <code>null</code> if none */ public static String getJavaProjectName(ILaunchConfiguration configuration) throws CoreException { return configuration.getAttribute( IJavaLaunchConfigurationConstants.ATTR_PROJECT_NAME, (String) null); } public static IGrailsInstall getGrailsInstall(ILaunchConfiguration conf) { GrailsInstallManager installMan = GrailsCoreActivator.getDefault().getInstallManager(); String installName = null; try { installName = conf.getAttribute(GRAILS_INSTALL_LAUNCH_ATTR, (String)null); } catch (CoreException e) { GrailsCoreActivator.log(e); } if (installName!=null) { return installMan.getGrailsInstall(installName); } else { IProject project = getProject(conf); return installMan.getGrailsInstall(project); } } public static void setGrailsInstall(ILaunchConfigurationWorkingCopy conf, IGrailsInstall install) { conf.setAttribute(GRAILS_INSTALL_LAUNCH_ATTR, install.getName()); } public static String getGrailsHome(ILaunchConfiguration configuration) { IGrailsInstall install = getGrailsInstall(configuration); if (install!=null) { return install.getHome(); } return null; } public static String getJavaHome(ILaunchConfiguration configuration) { try { IJavaProject javaProject = getJavaProject(configuration); IVMInstall vm = null; if (javaProject != null) { vm = JavaRuntime.getVMInstall(javaProject); } else { vm = JavaRuntime.getDefaultVMInstall(); } return vm.getInstallLocation().getAbsolutePath(); } catch (CoreException e) { GrailsCoreActivator.log(e); return null; } } /** * Extra classpath entries, telling grails where to classload our implementation of * GrailsBuildListener. */ public static List<String> getBuildListenerClassPath(ILaunchConfiguration conf) { String buildListener = getGrailsBuildListener(conf); if (buildListener==null) { return null; //If there's no build listener there's no need for this } else { IGrailsInstall install = getGrailsInstall(conf); Assert.isNotNull(install, "Can't determine Grails install for launch config"); GrailsVersion version = install.getVersion(); return getBuildListenerClassPath(version); } } /** * Determine which extra entries should be added to the runtime classpath for a given GrailsVersion. * This includes both Grails version specific as well as 'shared' bits of the BuildListener. * * @return Bundle-id */ public static List<String> getBuildListenerClassPath(GrailsVersion version) { ClasspathLocalizer localizer = new ClasspathLocalizer(); return localizer.localizeClasspath( new EclipsePluginClasspathEntry("org.grails.ide.eclipse.runtime.shared", null), new EclipsePluginClasspathEntry(getRuntimeBundleFor(version), null) ); } /** * Determine which version-specific runtime bundle should be added to the classpath for a given GrailsVersion. * * @return Bundle-id */ public static String getRuntimeBundleFor(GrailsVersion version) { if (version.compareTo(GrailsVersion.V_2_2_)>=0) { return "org.grails.ide.eclipse.runtime22"; } else if (version.compareTo(GrailsVersion.SMALLEST_SUPPORTED_VERSION)<=0) { throw new Error("This version of Grails no longer supported: "+version); } else { return "org.grails.ide.eclipse.runtime13"; } } public static void setSystemProperties( ILaunchConfigurationWorkingCopy launchConf, Map<String, String> systemProperties) { launchConf.setAttribute(SYSTEM_PROPERTIES_LAUNCH_ATTR, systemProperties); } @SuppressWarnings("unchecked") public static Map<String, String> getSystemProperties(ILaunchConfiguration conf) { try { return conf.getAttribute(SYSTEM_PROPERTIES_LAUNCH_ATTR, (Map<String, String>)null); } catch (CoreException e) { GrailsCoreActivator.log(e); return null; } } /** * Join a list of paths into a single String, with File.pathSeparator characters used to * separate the entries from one another. */ public static String toPathsString(List<String> paths) { StringBuffer buildListenerClassPath = new StringBuffer(); boolean first = true; for (String entry : paths) { if (!first) { buildListenerClassPath.append(File.pathSeparatorChar); } buildListenerClassPath.append(entry); first=false; } return buildListenerClassPath.toString(); } /** * Set property value in map, only if the property is not yet set in the map. */ public static void setMaybe(Map<String, String> props, String k, String v) { if (v!=null) { String existing = props.get(k); if (existing==null) { props.put(k, v); } } } public static final String[] proxyPropNames = { "http.proxyHost", "http.proxyPort", "http.nonProxyHosts", "https.proxyHost", "https.proxyPort", "https.nonProxyHosts", "ftp.proxyHost", "ftp.proxyPort", "ftp.nonProxyHosts" }; /** * Add proxy related system properties if needed. */ public static void addProxyProperties(Map<String, String> props) { for (String propName : proxyPropNames) { String sysProp = System.getProperty(propName); if (sysProp!=null) { setMaybe(props, propName, sysProp); } } } /** * Helper method to add some environment parameters to an existing String[] of env parameters. * Each String in the array is of the form "<key>=<value>". The <key> String shouldn't contain '='. * <p> * Code in JDT doesn't handle key values that have '=' in them, and so don't we. * <p> * If an entry already exists in the original, then it won't be added. This is to ensure that if * some user explicitly puts a customized env paramter setting in a launch config we will not * override it with our automatically added default. * * @param superEnv The original env we want to modify * @param extra Extra variables we want to define, in map form. * @return A copy of the original env with extra variables added. */ public static String[] addToEnvMaybe(String[] superEnv, Map<String, String> extra) { if (superEnv==null) { superEnv = new String[0]; } //Peeking at JDT code for merging env maps... it seems we need special handling of win32 platform, where the //keys are to be treated as case insensitive, but while preserving case at the same time. boolean win32= Platform.getOS().equals(Constants.OS_WIN32); Set<String> seenKeys = new HashSet<String>(superEnv.length+extra.size()); // separate set of case converted keys (for win32 handling) //copy existing and add keys to seenKeys ArrayList<String> newEnv = new ArrayList<String>(superEnv.length+extra.size()); for (String entry : superEnv) { int split = entry.indexOf('='); String key = entry.substring(0, split); String val = entry.substring(split+1); seenKeys.add(caseKey(key, win32)); newEnv.add(key+"="+val); } //add extra if not already in seenKeys for (Entry<String, String> entry : extra.entrySet()) { if (!seenKeys.contains(caseKey(entry.getKey(), win32))) { newEnv.add(entry.getKey()+"="+entry.getValue()); } } return newEnv.toArray(new String[newEnv.size()]); } /** * Convert a key used as an environment parameter to case(in)sensitive form depending on platform. * @param win32 True if we are on win32 platform */ private static String caseKey(String key, boolean win32) { return win32 ? key.toUpperCase() : key; } public static GrailsVersion getGrailsVersion(ILaunchConfiguration conf) { IGrailsInstall install = getGrailsInstall(conf); if (install!=null) { return install.getVersion(); } return GrailsVersion.UNKNOWN; } /** * Adds JVM arguments supplied by user in grails launch preferences page. */ public static void addUserDefinedJVMArgs(List<String> args) { try { String argsString = GrailsCoreActivator.getDefault().getJVMArgs(); if (argsString!=null) { String[] extraArgs = CommandLineUtil.translateCommandline(argsString); if (extraArgs!=null && extraArgs.length>0) { for (String arg : extraArgs) { args.add(arg); } } } } catch (CoreException e) { GrailsCoreActivator.log(e); } } }