462944 : set -Dmaven.multiModuleProjectDirectory in Launch configurations

... and load JVM configuration from .mvn/jvm.config when applicable, i.e. when the Maven runtime version is > 3.3

Change-Id: I1960246bb215ef2f452fcc0b7771e03b3fda4d80
Signed-off-by: Fred Bricon <fbricon@gmail.com>
diff --git a/org.eclipse.m2e.launching/META-INF/MANIFEST.MF b/org.eclipse.m2e.launching/META-INF/MANIFEST.MF
index 9232b71..4f4e1fd 100644
--- a/org.eclipse.m2e.launching/META-INF/MANIFEST.MF
+++ b/org.eclipse.m2e.launching/META-INF/MANIFEST.MF
@@ -24,7 +24,8 @@
  org.slf4j.api;bundle-version="1.6.2",
  org.eclipse.jdt.debug,
  org.eclipse.core.resources,
- org.eclipse.debug.core
+ org.eclipse.debug.core,
+ com.google.guava;bundle-version="[14.0.1,16.0.0)
 Bundle-ActivationPolicy: lazy
 Bundle-RequiredExecutionEnvironment: JavaSE-1.6,
  JavaSE-1.7
diff --git a/org.eclipse.m2e.launching/src/org/eclipse/m2e/internal/launch/MavenLaunchDelegate.java b/org.eclipse.m2e.launching/src/org/eclipse/m2e/internal/launch/MavenLaunchDelegate.java
index d3e010f..9164d87 100644
--- a/org.eclipse.m2e.launching/src/org/eclipse/m2e/internal/launch/MavenLaunchDelegate.java
+++ b/org.eclipse.m2e.launching/src/org/eclipse/m2e/internal/launch/MavenLaunchDelegate.java
@@ -14,22 +14,35 @@
 import static org.eclipse.m2e.internal.launch.MavenLaunchUtils.quote;
 
 import java.io.File;
+import java.io.IOException;
 import java.util.Collections;
 import java.util.List;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.common.base.Charsets;
+import com.google.common.io.Files;
+
 import org.eclipse.core.runtime.CoreException;
 import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
 import org.eclipse.debug.core.ILaunch;
 import org.eclipse.debug.core.ILaunchConfiguration;
 import org.eclipse.jdt.launching.IVMRunner;
 import org.eclipse.jdt.launching.JavaLaunchDelegate;
+import org.eclipse.osgi.util.NLS;
+
+import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
+import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException;
+import org.apache.maven.artifact.versioning.VersionRange;
 
 import org.eclipse.m2e.actions.MavenLaunchConstants;
 import org.eclipse.m2e.core.MavenPlugin;
 import org.eclipse.m2e.core.embedder.IMavenConfiguration;
+import org.eclipse.m2e.core.internal.IMavenConstants;
+import org.eclipse.m2e.core.internal.launch.AbstractMavenRuntime;
 import org.eclipse.m2e.internal.launch.MavenRuntimeLaunchSupport.VMArguments;
 
 
@@ -41,6 +54,18 @@
   //classworlds 2.0
   private static final String LAUNCHER_TYPE3 = "org.codehaus.plexus.classworlds.launcher.Launcher"; //$NON-NLS-1$
 
+  private static final VersionRange MAVEN_33PLUS_RUNTIMES;
+
+  static {
+    VersionRange mvn33PlusRange;
+    try {
+      mvn33PlusRange = VersionRange.createFromVersionSpec("[3.3,)");
+    } catch(InvalidVersionSpecificationException O_o) {
+      mvn33PlusRange = null;
+    }
+    MAVEN_33PLUS_RUNTIMES = mvn33PlusRange;
+  }
+
   private ILaunch launch;
 
   private IProgressMonitor monitor;
@@ -102,9 +127,13 @@
     return programArguments;
   }
 
+  @SuppressWarnings("restriction")
   public String getVMArguments(ILaunchConfiguration configuration) throws CoreException {
     VMArguments arguments = launchSupport.getVMArguments();
 
+    AbstractMavenRuntime runtime = MavenLaunchUtils.getMavenRuntime(configuration);
+    appendRuntimeSpecificArguments(runtime.getVersion(), arguments, configuration);
+
     extensionsSupport.appendVMArguments(arguments, configuration, launch, monitor);
 
     // user configured entries
@@ -230,4 +259,55 @@
   static void removeTempFiles(ILaunch launch) {
     MavenRuntimeLaunchSupport.removeTempFiles(launch);
   }
+
+  /**
+   * Not API. Made public for testing purposes.
+   */
+  public void appendRuntimeSpecificArguments(String runtimeVersion, VMArguments arguments,
+      ILaunchConfiguration configuration) throws CoreException {
+    if(applies(runtimeVersion)) {
+      getArgsFromMvnDir(arguments, configuration);
+    }
+  }
+
+  @SuppressWarnings("restriction")
+  private void getArgsFromMvnDir(VMArguments arguments, ILaunchConfiguration configuration) throws CoreException {
+    String pomDir = configuration.getAttribute(MavenLaunchConstants.ATTR_POM_DIR, "");
+    if(pomDir.isEmpty()) {
+      return;
+    }
+    File baseDir = findMavenProjectBasedir(new File(pomDir));
+    File mvnDir = new File(baseDir, ".mvn");
+    File jvmConfig = new File(mvnDir, "jvm.config");
+    if(jvmConfig.isFile()) {
+      try {
+        for(String line : Files.readLines(jvmConfig, Charsets.UTF_8)) {
+          arguments.append(line);
+        }
+      } catch(IOException ex) {
+        IStatus error = new Status(IStatus.ERROR, IMavenConstants.PLUGIN_ID, NLS.bind(
+            Messages.MavenLaunchDelegate_error_cannot_read_jvmConfig, jvmConfig.getAbsolutePath()), ex);
+        throw new CoreException(error);
+      }
+    }
+    arguments.appendProperty("maven.multiModuleProjectDirectory", MavenLaunchUtils.quote(baseDir.getAbsolutePath()));
+  }
+
+  //This will likely move to core when we need it
+  private File findMavenProjectBasedir(File dir) {
+    File folder = dir;
+    // loop upwards but stop if root
+    while(folder != null && folder.getParentFile() != null) {
+      // see if /.mvn exists
+      if(new File(folder, ".mvn").isDirectory()) {
+        return folder;
+      }
+      folder = folder.getParentFile();
+    }
+    return dir;
+  }
+
+  private boolean applies(String runtimeVersion) {
+    return MAVEN_33PLUS_RUNTIMES.containsVersion(new DefaultArtifactVersion(runtimeVersion));
+  }
 }
diff --git a/org.eclipse.m2e.launching/src/org/eclipse/m2e/internal/launch/Messages.java b/org.eclipse.m2e.launching/src/org/eclipse/m2e/internal/launch/Messages.java
index dd25d43..a244d16 100644
--- a/org.eclipse.m2e.launching/src/org/eclipse/m2e/internal/launch/Messages.java
+++ b/org.eclipse.m2e.launching/src/org/eclipse/m2e/internal/launch/Messages.java
@@ -33,6 +33,8 @@
 
   public static String MavenLaynchDelegate_unsupported_source_locator;
 
+  public static String MavenLaunchDelegate_error_cannot_read_jvmConfig;
+
   public static String MavenLaunchMainTab_btnAfterClean;
 
   public static String MavenLaunchMainTab_btnAutoBuild;
@@ -110,7 +112,9 @@
   public static String launchPomDirectoryEmpty;
 
   public static String launchPomDirectoryDoesntExist;
+
   public static String MavenLaunchMainTab_lblUserSettings_text;
+
   public static String MavenLaunchMainTab_btnUserSettings_text;
 
   static {
diff --git a/org.eclipse.m2e.launching/src/org/eclipse/m2e/internal/launch/messages.properties b/org.eclipse.m2e.launching/src/org/eclipse/m2e/internal/launch/messages.properties
index d38c773..60f1f5f 100644
--- a/org.eclipse.m2e.launching/src/org/eclipse/m2e/internal/launch/messages.properties
+++ b/org.eclipse.m2e.launching/src/org/eclipse/m2e/internal/launch/messages.properties
@@ -5,8 +5,9 @@
 ExecutePomAction_dialog_title=Select Configuration
 ExecutePomAction_executing=Executing {0} in {1}
 MavenFileEditorInput_0=Unable to open {0}
-MavenLaunchDelegate_error_cannot_create_conf=Can't create m2.conf
+MavenLaunchDelegate_error_cannot_create_conf=Can not create m2.conf
 MavenLaunchDelegate_job_name=Refreshing resources...
+MavenLaunchDelegate_error_cannot_read_jvmConfig=Can not read JVM configuration from {0}
 MavenLaunchExtensionsTab_lblExtensions=Maven Launch Extensions
 MavenLaunchExtensionsTab_name=Launch Extensions
 MavenLaunchMainTab_btnAfterClean=Selec&t...