IDEA-266058 - fix maven connector spawn for multimodule projects

GitOrigin-RevId: 1eaeb481b3cb559bd26c1163442d2213225a0e79
This commit is contained in:
Alexander Bubenchikov
2021-04-14 23:37:43 +03:00
committed by intellij-monorepo-bot
parent c5e725de05
commit aa07d72bc6
9 changed files with 232 additions and 34 deletions
@@ -63,10 +63,11 @@ public class MavenDistributionsCache {
}
public @NotNull String getVmOptions(@Nullable String workingDirectory) {
if (!useWrapper() || workingDirectory == null) {
return MavenWorkspaceSettingsComponent.getInstance(myProject).getSettings().importingSettings.getVmOptionsForImporter();
String vmOptions = MavenWorkspaceSettingsComponent.getInstance(myProject).getSettings().getImportingSettings().getVmOptionsForImporter();
if (workingDirectory == null || !StringUtil.isEmptyOrSpaces(vmOptions)) {
return vmOptions;
}
String multiModuleDir = myWorkingDirToMultimoduleMap.computeIfAbsent(workingDirectory, this::resolveMultimoduleDirectory);
return myVmSettingsMap.computeIfAbsent(multiModuleDir, this::readVmOptions);
}
@@ -74,17 +75,17 @@ public class MavenDistributionsCache {
private @NotNull String readVmOptions(@NotNull String multiModuleDir) {
MavenWorkspaceSettings settings = MavenWorkspaceSettingsComponent.getInstance(myProject).getSettings();
VirtualFile baseDir = LocalFileSystem.getInstance().findFileByPath(multiModuleDir);
if (baseDir == null) return settings.importingSettings.getVmOptionsForImporter();
if (baseDir == null) return settings.getImportingSettings().getVmOptionsForImporter();
VirtualFile mvn = baseDir.findChild(".mvn");
if (mvn == null) return settings.importingSettings.getVmOptionsForImporter();
if (mvn == null) return settings.getImportingSettings().getVmOptionsForImporter();
VirtualFile jdkOpts = mvn.findChild("jvm.config");
if (jdkOpts == null) return settings.importingSettings.getVmOptionsForImporter();
if (jdkOpts == null) return settings.getImportingSettings().getVmOptionsForImporter();
try {
return new String(jdkOpts.contentsToByteArray(true), jdkOpts.getCharset());
}
catch (IOException e) {
MavenLog.LOG.warn(e);
return settings.importingSettings.getVmOptionsForImporter();
return settings.getImportingSettings().getVmOptionsForImporter();
}
}
@@ -165,7 +166,7 @@ public class MavenDistributionsCache {
.map(p -> p.getDirectory())
.filter(rpDirectory -> FileUtil.isAncestor(rpDirectory, workingDirectory, false))
.findFirst()
.orElse(workingDirectory));
.orElse(calculateMultimoduleDirUpToFileTree(workingDirectory)));
}
private @NotNull String calculateMultimoduleDirUpToFileTree(String directory) {
@@ -12,7 +12,7 @@ import org.jetbrains.idea.maven.model.MavenModel;
import java.io.File;
import java.rmi.RemoteException;
import java.util.Collection;
import java.util.*;
public abstract class MavenServerConnector implements @NotNull Disposable {
public static final Logger LOG = Logger.getInstance(MavenServerConnector.class);
@@ -21,7 +21,7 @@ public abstract class MavenServerConnector implements @NotNull Disposable {
protected final MavenServerManager myManager;
protected @NotNull final MavenDistribution myDistribution;
protected final Sdk myJdk;
protected final String myMultimoduleDirectory;
protected final Set<String> myMultimoduleDirectories;
protected final String myVmOptions;
@@ -36,7 +36,12 @@ public abstract class MavenServerConnector implements @NotNull Disposable {
myDistribution = mavenDistribution;
myVmOptions = vmOptions;
myJdk = jdk;
myMultimoduleDirectory = multimoduleDirectory;
myMultimoduleDirectories = new LinkedHashSet<>();
myMultimoduleDirectories.add(multimoduleDirectory);
}
boolean addMultimoduleDir(String multimoduleDirectory) {
return myMultimoduleDirectories.add(multimoduleDirectory);
}
abstract boolean isNew();
@@ -132,7 +137,7 @@ public abstract class MavenServerConnector implements @NotNull Disposable {
return myProject;
}
public String getMultimoduleDirectory() {
return myMultimoduleDirectory;
public List<String> getMultimoduleDirectories() {
return new ArrayList<>(myMultimoduleDirectories);
}
}
@@ -235,7 +235,8 @@ public class MavenServerConnectorImpl extends MavenServerConnector {
@Override
public void run() {
ProgressIndicator indicator = new EmptyProgressIndicator();
MavenLog.LOG.info("Connecting maven connector in " + myMultimoduleDirectory);
String dirForLogs = myMultimoduleDirectories.iterator().next();
MavenLog.LOG.info("Connecting maven connector in " + dirForLogs);
try {
if (myDebugPort != null) {
//noinspection UseOfSystemOutOrSystemErr
@@ -253,10 +254,10 @@ public class MavenServerConnectorImpl extends MavenServerConnector {
server.set(myLogger, myDownloadListener, MavenRemoteObjectWrapper.ourToken);
myServerPromise.setResult(server);
MavenLog.LOG.info("Connector in " + myMultimoduleDirectory + " has been connected");
MavenLog.LOG.info("Connector in " + dirForLogs + " has been connected");
}
catch (Throwable e) {
MavenLog.LOG.warn("Cannot connect connector in " + myMultimoduleDirectory, e);
MavenLog.LOG.warn("Cannot connect connector in " + dirForLogs, e);
myServerPromise.setError(e);
}
}
@@ -62,9 +62,11 @@ public final class MavenServerManager implements Disposable {
}
public Collection<MavenServerConnector> getAllConnectors() {
Set<MavenServerConnector> set = Collections.newSetFromMap(new IdentityHashMap<>());
synchronized (myMultimoduleDirToConnectorMap) {
return new ArrayList<>(myMultimoduleDirToConnectorMap.values());
set.addAll(myMultimoduleDirToConnectorMap.values());
}
return set;
}
public void cleanUp(MavenServerConnector connector) {
@@ -126,28 +128,60 @@ public final class MavenServerManager implements Disposable {
MavenWorkspaceSettings settings = MavenWorkspaceSettingsComponent.getInstance(project).getSettings();
Sdk jdk = getJdk(project, settings);
MavenServerConnector connector;
synchronized (myMultimoduleDirToConnectorMap) {
connector = myMultimoduleDirToConnectorMap.computeIfAbsent(multimoduleDirectory, dir -> registerNewConnector(project, jdk, multimoduleDirectory));
}
MavenServerConnector connector = doGetOrCreateConnector(project, multimoduleDirectory, jdk);
if(connector.isNew()) {
connector.connect();
} else {
if(!compatibleParameters(project, connector, jdk, multimoduleDirectory)) {
if (!compatibleParameters(project, connector, jdk, multimoduleDirectory)) {
MavenLog.LOG.info("Maven connector in " + multimoduleDirectory + " is incompatible, restarting");
connector.shutdown(false);
synchronized (myMultimoduleDirToConnectorMap) {
connector = myMultimoduleDirToConnectorMap.computeIfAbsent(multimoduleDirectory, dir -> registerNewConnector(project, jdk, multimoduleDirectory));
}
connector = this.doGetOrCreateConnector(project, multimoduleDirectory, jdk);
connector.connect();
}
}
return connector;
}
private MavenServerConnector registerNewConnector(Project project,
Sdk jdk,
String multimoduleDirectory) {
private MavenServerConnector doGetOrCreateConnector(@NotNull Project project,
@NotNull String multimoduleDirectory,
@NotNull Sdk jdk) {
MavenServerConnector connector;
synchronized (myMultimoduleDirToConnectorMap) {
connector = myMultimoduleDirToConnectorMap.get(multimoduleDirectory);
if (connector != null) return connector;
connector = findCompatibleConnector(project, jdk, multimoduleDirectory);
if (connector != null) {
MavenLog.LOG.info("use existing connector for " + multimoduleDirectory + ":::" + connector.getMultimoduleDirectories());
connector.addMultimoduleDir(multimoduleDirectory);
}
else {
connector = registerNewConnector(project, jdk, multimoduleDirectory);
}
myMultimoduleDirToConnectorMap.put(multimoduleDirectory, connector);
}
return connector;
}
private @Nullable MavenServerConnector findCompatibleConnector(@NotNull Project project,
@NotNull Sdk jdk,
@NotNull String multimoduleDirectory) {
MavenDistribution distribution = MavenDistributionsCache.getInstance(project).getMavenDistribution(multimoduleDirectory);
String vmOptions = MavenDistributionsCache.getInstance(project).getVmOptions(multimoduleDirectory);
for (Map.Entry<String, MavenServerConnector> entry : myMultimoduleDirToConnectorMap.entrySet()) {
if (!entry.getValue().getProject().equals(project)) continue;
if (Registry.is("maven.server.per.idea.project")) return entry.getValue();
if (entry.getValue().isCompatibleWith(jdk, vmOptions, distribution)) {
return entry.getValue();
}
}
return null;
}
private @NotNull MavenServerConnector registerNewConnector(Project project,
Sdk jdk,
String multimoduleDirectory) {
MavenDistribution distribution = MavenDistributionsCache.getInstance(project).getMavenDistribution(multimoduleDirectory);
String vmOptions = MavenDistributionsCache.getInstance(project).getVmOptions(multimoduleDirectory);
Integer debugPort = getDebugPort(project);
@@ -210,7 +244,7 @@ public final class MavenServerManager implements Disposable {
MavenServerConnector connector,
Sdk jdk,
String multimoduleDirectory) {
if (Registry.is("maven.server.per.idea.project")) return true;
MavenDistributionsCache cache = MavenDistributionsCache.getInstance(project);
MavenDistribution distribution = cache.getMavenDistribution(multimoduleDirectory);
String vmOptions = cache.getVmOptions(multimoduleDirectory);
@@ -436,8 +470,11 @@ public final class MavenServerManager implements Disposable {
protected MavenServerIndexer create() throws RemoteException {
MavenServerConnector connector;
synchronized (myMultimoduleDirToConnectorMap) {
connector = ContainerUtil.find(myMultimoduleDirToConnectorMap.values(), c -> FileUtil
.isAncestor(finalPath, c.getMultimoduleDirectory(), false));
connector = ContainerUtil.find(myMultimoduleDirToConnectorMap.values(), c -> ContainerUtil.find(
c.myMultimoduleDirectories,
mDir -> FileUtil
.isAncestor(finalPath, mDir, false)) != null
);
}
if (connector != null) {
return connector.createIndexer();
@@ -49,7 +49,7 @@ class ConnectorTable : ListTableWithButtons<MavenServerConnector>() {
val project = TableColumn(MavenConfigurableBundle.message("connector.ui.project")) { it.project.name }
val jdk = TableColumn(MavenConfigurableBundle.message("connector.ui.jdk")) { it.jdk.name }
val vmopts = TableColumn(MavenConfigurableBundle.message("connector.ui.vmOptions")) { it.vmOptions }
val dir = TableColumn(MavenConfigurableBundle.message("connector.ui.dir")) { it.multimoduleDirectory }
val dir = TableColumn(MavenConfigurableBundle.message("connector.ui.dir")) { it.multimoduleDirectories.joinToString(separator = ",") }
val maven = TableColumn(
MavenConfigurableBundle.message("connector.ui.maven")) { "${it.mavenDistribution.version} ${it.mavenDistribution.mavenHome}" }
val state = TableColumn(
@@ -3,9 +3,10 @@ package org.jetbrains.idea.maven.server.ui
import com.intellij.openapi.actionSystem.AnAction
import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.project.DumbAwareAction
import org.jetbrains.idea.maven.statistics.MavenActionsUsagesCollector
class ShowMavenConnectorsAction : AnAction() {
class ShowMavenConnectorsAction : DumbAwareAction() {
override fun actionPerformed(e: AnActionEvent) {
MavenActionsUsagesCollector.trigger(e.project, MavenActionsUsagesCollector.ActionID.ShowMavenConnectors)
MavenConnectorList().show()
@@ -275,11 +275,17 @@ public class MavenUtil {
VirtualFile child = dir.findChild(".mvn");
if (child != null && child.isDirectory()) {
if (MavenLog.LOG.isDebugEnabled()) {
MavenLog.LOG.debug("found .mvn in " + child);
}
baseDir = dir;
break;
}
}
while ((dir = dir.getParent()) != null);
if (MavenLog.LOG.isDebugEnabled()) {
MavenLog.LOG.debug("return " + baseDir + " as baseDir");
}
return baseDir;
}
@@ -253,8 +253,11 @@
<registryKey key="maven.import.compiler.arguments" defaultValue="true"
description="Enable importing of the maven compiler plugin arguments"/>
<registryKey key="maven.server.per.idea.project" defaultValue="false"
description="Single Maven server per idea project, .mvn could be ignored"/>
<registryKey key="maven.always.reset" defaultValue="false"
description="always reset maven dependencies and folders"/>
description="Always reset maven dependencies and folders"/>
<registryKey key="maven.print.import.stacktraces" defaultValue="false"
description="Print maven server stacktraces in log"/>
@@ -0,0 +1,144 @@
// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package org.jetbrains.idea.maven.importing
import com.intellij.openapi.util.io.FileUtil
import com.intellij.openapi.util.registry.Registry
import com.intellij.openapi.vfs.LocalFileSystem
import com.intellij.openapi.vfs.VirtualFile
import org.jetbrains.idea.maven.MavenImportingTestCase
import org.jetbrains.idea.maven.server.MavenServerManager
import org.jetbrains.idea.maven.wizards.MavenOpenProjectProvider
import java.io.File
class MavenImportingConnectorsTest : MavenImportingTestCase() {
protected lateinit var myAnotherProjectRoot: VirtualFile
@Throws(Exception::class)
override fun setUpInWriteAction() {
super.setUpInWriteAction()
val projectDir = File(myDir, "anotherProject")
projectDir.mkdirs()
myAnotherProjectRoot = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(projectDir)!!
}
fun testShouldNotCreateNewConnectorForNewProject() {
createProjectPom("<groupId>test</groupId>" +
"<artifactId>project1</artifactId>" +
"<version>1</version>" +
"<packaging>pom</packaging>" +
"<modules>" +
"<module>m1</module>" +
" </modules>")
createModulePom("m1", "<groupId>test</groupId>" +
"<artifactId>m1</artifactId>" +
"<version>1</version>")
importProject()
assertModules("project1", "m1")
val p2Root = createPomFile(myAnotherProjectRoot, "<groupId>test</groupId>" +
"<artifactId>project2</artifactId>" +
"<version>1</version>" +
"<packaging>pom</packaging>" +
"<modules>" +
"<module>m2</module>" +
" </modules>")
createModulePom("../anotherProject/m2", "<groupId>test</groupId>" +
"<artifactId>m2</artifactId>" +
"<version>2</version>")
MavenOpenProjectProvider().linkToExistingProject(p2Root, myProject)
assertModules("project1", "m1", "project2", "m2")
assertEquals(1, MavenServerManager.getInstance().allConnectors.size);
assertUnorderedElementsAreEqual(
MavenServerManager.getInstance().allConnectors.first().multimoduleDirectories.map {
FileUtil.getRelativePath(myDir, File(it))
},
listOf("project", "anotherProject")
)
}
fun testShouldCreateNewConnectorForNewProjectIfJvmConfigPresents() {
createProjectPom("<groupId>test</groupId>" +
"<artifactId>project1</artifactId>" +
"<version>1</version>" +
"<packaging>pom</packaging>" +
"<modules>" +
"<module>m1</module>" +
" </modules>")
createModulePom("m1", "<groupId>test</groupId>" +
"<artifactId>m1</artifactId>" +
"<version>1</version>")
importProject()
assertModules("project1", "m1")
val p2Root = createPomFile(myAnotherProjectRoot, "<groupId>test</groupId>" +
"<artifactId>project2</artifactId>" +
"<version>1</version>" +
"<packaging>pom</packaging>" +
"<modules>" +
"<module>m2</module>" +
" </modules>")
createModulePom("../anotherProject/m2", "<groupId>test</groupId>" +
"<artifactId>m2</artifactId>" +
"<version>2</version>")
createProjectSubFile("../anotherProject/.mvn/jvm.config", "-Dsomething=blablabla")
MavenOpenProjectProvider().linkToExistingProject(p2Root, myProject)
assertModules("project1", "m1", "project2", "m2")
assertEquals(2, MavenServerManager.getInstance().allConnectors.size);
assertUnorderedElementsAreEqual(
MavenServerManager.getInstance().allConnectors.map {
FileUtil.getRelativePath(myDir, File(it.multimoduleDirectories.first()))
},
listOf("project", "anotherProject")
)
}
fun testShouldNotCreateNewConnectorForNewProjectIfJvmConfigPresentsAndRegistrySet() {
createProjectPom("<groupId>test</groupId>" +
"<artifactId>project1</artifactId>" +
"<version>1</version>" +
"<packaging>pom</packaging>" +
"<modules>" +
"<module>m1</module>" +
" </modules>")
createModulePom("m1", "<groupId>test</groupId>" +
"<artifactId>m1</artifactId>" +
"<version>1</version>")
importProject()
assertModules("project1", "m1")
val p2Root = createPomFile(myAnotherProjectRoot, "<groupId>test</groupId>" +
"<artifactId>project2</artifactId>" +
"<version>1</version>" +
"<packaging>pom</packaging>" +
"<modules>" +
"<module>m2</module>" +
" </modules>")
createModulePom("../anotherProject/m2", "<groupId>test</groupId>" +
"<artifactId>m2</artifactId>" +
"<version>2</version>")
createProjectSubFile("../anotherProject/.mvn/jvm.config", "-Dsomething=blablabla")
val value = Registry.`is`("maven.server.per.idea.project")
try {
Registry.get("maven.server.per.idea.project").setValue(true);
MavenOpenProjectProvider().linkToExistingProject(p2Root, myProject)
assertModules("project1", "m1", "project2", "m2")
assertEquals(1, MavenServerManager.getInstance().allConnectors.size);
assertUnorderedElementsAreEqual(
MavenServerManager.getInstance().allConnectors.first().multimoduleDirectories.map {
FileUtil.getRelativePath(myDir, File(it))
},
listOf("project", "anotherProject")
)
}
finally {
Registry.get("maven.server.per.idea.project").setValue(value)
}
}
}