diff --git a/plugins/maven-server-api/src/main/java/org/jetbrains/idea/maven/server/MavenServerExecutionResult.java b/plugins/maven-server-api/src/main/java/org/jetbrains/idea/maven/server/MavenServerExecutionResult.java index 295a30155e70..80fb60e71803 100644 --- a/plugins/maven-server-api/src/main/java/org/jetbrains/idea/maven/server/MavenServerExecutionResult.java +++ b/plugins/maven-server-api/src/main/java/org/jetbrains/idea/maven/server/MavenServerExecutionResult.java @@ -22,14 +22,12 @@ import org.jetbrains.idea.maven.model.MavenModel; import org.jetbrains.idea.maven.model.MavenProjectProblem; import java.io.Serializable; -import java.util.Collection; -import java.util.Collections; -import java.util.Map; -import java.util.Set; +import java.util.*; public class MavenServerExecutionResult implements Serializable { - public static final MavenServerExecutionResult EMPTY = new MavenServerExecutionResult(null, Collections.emptyList(), Collections.emptySet()); + public static final MavenServerExecutionResult EMPTY = + new MavenServerExecutionResult(null, Collections.emptyList(), Collections.emptySet()); @Nullable public final ProjectData projectData; @NotNull public final Collection problems; @@ -55,17 +53,21 @@ public class MavenServerExecutionResult implements Serializable { public static class ProjectData implements Serializable { @NotNull public final MavenModel mavenModel; + @NotNull + public final List managedDependencies; public final String dependencyHash; public final boolean dependencyResolutionSkipped; public final Map mavenModelMap; public final Collection activatedProfiles; public ProjectData(@NotNull MavenModel mavenModel, + @NotNull List managedDependencies, @Nullable String dependencyHash, boolean dependencyResolutionSkipped, Map mavenModelMap, Collection activatedProfiles) { this.mavenModel = mavenModel; + this.managedDependencies = managedDependencies; this.dependencyHash = dependencyHash; this.dependencyResolutionSkipped = dependencyResolutionSkipped; this.mavenModelMap = mavenModelMap; diff --git a/plugins/maven/maven3-server-impl/src/org/jetbrains/idea/maven/server/utils/Maven3XProjectResolver.java b/plugins/maven/maven3-server-impl/src/org/jetbrains/idea/maven/server/utils/Maven3XProjectResolver.java index f502ef87830a..623abd23a556 100644 --- a/plugins/maven/maven3-server-impl/src/org/jetbrains/idea/maven/server/utils/Maven3XProjectResolver.java +++ b/plugins/maven/maven3-server-impl/src/org/jetbrains/idea/maven/server/utils/Maven3XProjectResolver.java @@ -37,6 +37,7 @@ import org.jetbrains.idea.maven.server.embedder.CustomMaven3ModelInterpolator2; import java.io.File; import java.util.*; import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; import static org.jetbrains.idea.maven.server.MavenServerEmbedder.MAVEN_EMBEDDER_VERSION; @@ -199,7 +200,8 @@ public class Maven3XProjectResolver { if (myLongRunningTask.isCanceled()) return MavenServerExecutionResult.EMPTY; MavenServerExecutionResult result = myTelemetry.callWithSpan( "resolveBuildingResult " + br.projectId, () -> - resolveBuildingResult(repositorySession, addUnresolved, br.mavenProject, br.modelProblems, br.exceptions, br.dependencyHash)); + resolveBuildingResult(repositorySession, addUnresolved, br.mavenProject, br.modelProblems, br.exceptions, + br.dependencyHash)); myLongRunningTask.incrementFinishedRequests(); return result; } @@ -213,7 +215,9 @@ public class Maven3XProjectResolver { return executionResults; } - private boolean transitiveDependenciesChanged(@NotNull File pomFile, String newDependencyHash, Map fileToNewDependencyHash) { + private boolean transitiveDependenciesChanged(@NotNull File pomFile, + String newDependencyHash, + Map fileToNewDependencyHash) { if (dependenciesChanged(pomFile, newDependencyHash)) return true; for (File dependencyPomFile : myPomHashMap.getFileDependencies(pomFile)) { if (dependenciesChanged(dependencyPomFile, fileToNewDependencyHash.get(dependencyPomFile))) return true; @@ -345,12 +349,27 @@ public class Maven3XProjectResolver { Map mavenModelMap = Maven3ModelConverter.convertToMap(mavenProject.getModel()); MavenServerExecutionResult.ProjectData data = - new MavenServerExecutionResult.ProjectData(model, dependencyHash, dependencyResolutionSkipped, mavenModelMap, activatedProfiles); + new MavenServerExecutionResult.ProjectData(model, getManagedDependencies(mavenProject), dependencyHash, dependencyResolutionSkipped, + mavenModelMap, activatedProfiles); Collection unresolvedProblems = new HashSet<>(); collectUnresolvedArtifactProblems(file, dependencyResolutionResult, unresolvedProblems); return new MavenServerExecutionResult(data, problems, Collections.emptySet(), unresolvedProblems); } + @NotNull + private static List getManagedDependencies(@Nullable MavenProject project) { + + if (project == null || + project.getDependencyManagement() == null || + project.getDependencyManagement().getDependencies() == null) { + return Collections.emptyList(); + } + //noinspection SSBasedInspection + return project.getDependencyManagement().getDependencies().stream().map( + dep -> new MavenId(dep.getGroupId(), dep.getArtifactId(), dep.getVersion()) + ).collect(Collectors.toList()); + } + private static void fillSessionCache(MavenSession mavenSession, RepositorySystemSession session, List buildingResults) { diff --git a/plugins/maven/maven40-server-impl/src/com/intellij/maven/server/m40/utils/Maven40ProjectResolver.java b/plugins/maven/maven40-server-impl/src/com/intellij/maven/server/m40/utils/Maven40ProjectResolver.java index 9e2f1c17b6c0..e1b652c77f24 100644 --- a/plugins/maven/maven40-server-impl/src/com/intellij/maven/server/m40/utils/Maven40ProjectResolver.java +++ b/plugins/maven/maven40-server-impl/src/com/intellij/maven/server/m40/utils/Maven40ProjectResolver.java @@ -32,6 +32,7 @@ import org.jetbrains.idea.maven.server.PomHashMap; import java.io.File; import java.util.*; import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; public class Maven40ProjectResolver { @NotNull private final Maven40ServerEmbedderImpl myEmbedder; @@ -194,7 +195,9 @@ public class Maven40ProjectResolver { return executionResults; } - private boolean transitiveDependenciesChanged(@NotNull File pomFile, String newDependencyHash, Map fileToNewDependencyHash) { + private boolean transitiveDependenciesChanged(@NotNull File pomFile, + String newDependencyHash, + Map fileToNewDependencyHash) { if (dependenciesChanged(pomFile, newDependencyHash)) return true; for (File dependencyPomFile : myPomHashMap.getFileDependencies(pomFile)) { if (dependenciesChanged(dependencyPomFile, fileToNewDependencyHash.get(dependencyPomFile))) return true; @@ -288,7 +291,8 @@ public class Maven40ProjectResolver { @NotNull private MavenServerExecutionResult createExecutionResult(@NotNull MavenProject mavenProject, String dependencyHash) { - return createExecutionResult(mavenProject.getFile(), Collections.emptyList(), Collections.emptyList(), mavenProject, null, dependencyHash, true); + return createExecutionResult(mavenProject.getFile(), Collections.emptyList(), Collections.emptyList(), mavenProject, null, + dependencyHash, true); } @NotNull @@ -357,13 +361,28 @@ public class Maven40ProjectResolver { Map mavenModelMap = Maven40ModelConverter.convertToMap(mavenProject.getModel()); MavenServerExecutionResult.ProjectData data = - new MavenServerExecutionResult.ProjectData(model, dependencyHash, dependencyResolutionSkipped, mavenModelMap, activatedProfiles); + new MavenServerExecutionResult.ProjectData(model, getManagedDependencies(mavenProject), dependencyHash, dependencyResolutionSkipped, + mavenModelMap, activatedProfiles); if (null == model.getBuild() || null == model.getBuild().getDirectory()) { data = null; } return new MavenServerExecutionResult(data, problems, Collections.emptySet(), unresolvedProblems); } + @NotNull + private static List getManagedDependencies(@Nullable MavenProject project) { + + if (project == null || + project.getDependencyManagement() == null || + project.getDependencyManagement().getDependencies() == null) { + return Collections.emptyList(); + } + //noinspection SSBasedInspection + return project.getDependencyManagement().getDependencies().stream().map( + dep -> new MavenId(dep.getGroupId(), dep.getArtifactId(), dep.getVersion()) + ).collect(Collectors.toList()); + } + private void collectUnresolvedArtifactProblems(@Nullable File file, @Nullable DependencyResolutionResult result, diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenProject.kt b/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenProject.kt index 33bd80dfb0ba..6a930f08a1ce 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenProject.kt +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenProject.kt @@ -35,6 +35,7 @@ import java.io.* import java.util.* import java.util.concurrent.ConcurrentHashMap import java.util.function.Predicate +import kotlin.Throws class MavenProject(val file: VirtualFile) { enum class ConfigFileKind(val myRelativeFilePath: String, val myValueIfMissing: String) { @@ -79,6 +80,7 @@ class MavenProject(val file: VirtualFile) { myState, true, readerResult.mavenModel, + emptyList(), readerResult.readingProblems, readerResult.activatedProfiles, setOf(), @@ -98,6 +100,7 @@ class MavenProject(val file: VirtualFile) { @Internal fun updateState( model: MavenModel, + managedDependencies: List, dependencyHash: String?, readingProblems: Collection, activatedProfiles: MavenExplicitProfiles, @@ -111,6 +114,7 @@ class MavenProject(val file: VirtualFile) { myState, false, model, + managedDependencies, readingProblems, activatedProfiles, unresolvedArtifactIds, @@ -139,7 +143,7 @@ class MavenProject(val file: VirtualFile) { @Internal fun updateState(readingProblems: Collection): MavenProjectChanges { - val newState= myState.copy(readingProblems = readingProblems) + val newState = myState.copy(readingProblems = readingProblems) return setState(newState) } @@ -714,6 +718,8 @@ class MavenProject(val file: VirtualFile) { setState(newState) } + fun findManagedDependency(groupId: String, artifactId: String): MavenId? = myState.managedDependencies["$groupId:$artifactId"] + fun findDependencies(depProject: MavenProject): List { return findDependencies(depProject.mavenId) } @@ -856,10 +862,10 @@ class MavenProject(val file: VirtualFile) { if (configs.size == 1) return getCompilerLevel(level, configs[0]) return configs - .mapNotNull { findChildValueByPath(it, level) } - .map { LanguageLevel.parse(it) ?: LanguageLevel.HIGHEST } - .maxWithOrNull(Comparator.naturalOrder()) - ?.toJavaVersion()?.toFeatureString() ?: myState.properties!!.getProperty("maven.compiler.$level") + .mapNotNull { findChildValueByPath(it, level) } + .map { LanguageLevel.parse(it) ?: LanguageLevel.HIGHEST } + .maxWithOrNull(Comparator.naturalOrder()) + ?.toJavaVersion()?.toFeatureString() ?: myState.properties!!.getProperty("maven.compiler.$level") } private fun getCompilerLevel(level: String, config: Element): String? { @@ -1104,6 +1110,7 @@ class MavenProject(val file: VirtualFile) { state: MavenProjectState, incLastReadStamp: Boolean, model: MavenModel, + managedDependencies: List, readingProblems: Collection, activatedProfiles: MavenExplicitProfiles, unresolvedArtifactIds: Set, @@ -1126,6 +1133,7 @@ class MavenProject(val file: VirtualFile) { val newPluginInfos = LinkedHashSet() val newExtensions = LinkedHashSet() val newAnnotationProcessors = LinkedHashSet() + val newManagedDeps = LinkedHashMap() if (keepPreviousArtifacts) { newUnresolvedArtifacts.addAll(state.unresolvedArtifactIds) @@ -1135,6 +1143,7 @@ class MavenProject(val file: VirtualFile) { newDependencyTree.addAll(state.dependencyTree) newExtensions.addAll(state.extensions) newAnnotationProcessors.addAll(state.annotationProcessors) + newManagedDeps.putAll(state.managedDependencies) } // either keep all previous plugins or only those that are present in the new list @@ -1154,6 +1163,7 @@ class MavenProject(val file: VirtualFile) { newDependencyTree.addAll(model.dependencyTree) newDependencies.addAll(model.dependencies) newExtensions.addAll(model.extensions) + managedDependencies.forEach { md -> newManagedDeps.put("${md.groupId}:${md.artifactId}", md) } val remoteRepositories = ArrayList(newRepositories) val remotePluginRepositories = ArrayList(newPluginRepositories) @@ -1162,6 +1172,7 @@ class MavenProject(val file: VirtualFile) { val pluginInfos = ArrayList(newPluginInfos) val extensions = ArrayList(newExtensions) val annotationProcessors = ArrayList(newAnnotationProcessors) + val managedDependenciesMap = LinkedHashMap(newManagedDeps) val newDependencyHash = dependencyHash ?: state.dependencyHash val lastReadStamp = state.lastReadStamp + if (incLastReadStamp) 1 else 0 @@ -1199,6 +1210,7 @@ class MavenProject(val file: VirtualFile) { pluginInfos = pluginInfos, extensions = extensions, annotationProcessors = annotationProcessors, + managedDependencies = managedDependenciesMap, dependencyHash = newDependencyHash, ) } diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenProjectResolver.kt b/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenProjectResolver.kt index a2d640feecf6..9df0af9b7843 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenProjectResolver.kt +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenProjectResolver.kt @@ -38,14 +38,17 @@ import java.util.concurrent.ConcurrentLinkedQueue data class MavenProjectResolutionResult(val mavenProjectMap: Map>) @ApiStatus.Internal -class MavenProjectResolverResult(@JvmField val mavenModel: MavenModel, - @JvmField val dependencyHash: String?, - @JvmField val dependencyResolutionSkipped: Boolean, - @JvmField val nativeModelMap: Map, - @JvmField val activatedProfiles: MavenExplicitProfiles, - @JvmField val readingProblems: MutableCollection, - @JvmField val unresolvedArtifactIds: MutableSet, - val unresolvedProblems: Collection) +class MavenProjectResolverResult( + @JvmField val mavenModel: MavenModel, + @JvmField val managedDependencies: List, + @JvmField val dependencyHash: String?, + @JvmField val dependencyResolutionSkipped: Boolean, + @JvmField val nativeModelMap: Map, + @JvmField val activatedProfiles: MavenExplicitProfiles, + @JvmField val readingProblems: MutableCollection, + @JvmField val unresolvedArtifactIds: MutableSet, + val unresolvedProblems: Collection, +) @ApiStatus.Internal interface MavenProjectResolutionContributor { @@ -73,14 +76,16 @@ interface MavenProjectResolutionContributor { @ApiStatus.Internal class MavenProjectResolver(private val myProject: Project) { - suspend fun resolve(incrementally: Boolean, - mavenProjects: Collection, - tree: MavenProjectsTree, - workspaceMap: MavenWorkspaceMap, - generalSettings: MavenGeneralSettings, - embeddersManager: MavenEmbeddersManager, - progressReporter: RawProgressReporter, - eventHandler: MavenEventHandler): MavenProjectResolutionResult { + suspend fun resolve( + incrementally: Boolean, + mavenProjects: Collection, + tree: MavenProjectsTree, + workspaceMap: MavenWorkspaceMap, + generalSettings: MavenGeneralSettings, + embeddersManager: MavenEmbeddersManager, + progressReporter: RawProgressReporter, + eventHandler: MavenEventHandler, + ): MavenProjectResolutionResult { val updateSnapshots = MavenProjectsManager.getInstance(myProject).forceUpdateSnapshots || generalSettings.isAlwaysUpdateSnapshots val projectsWithUnresolvedPlugins = HashMap>() val projectMultiMap = MavenUtil.groupByBasedir(mavenProjects, tree) @@ -324,7 +329,7 @@ class MavenProjectResolver(private val myProject: Project) { eventHandler: MavenEventHandler, workspaceMap: MavenWorkspaceMap?, updateSnapshots: Boolean, - userProperties: Properties + userProperties: Properties, ): Pair, Collection> { val files = pomToDependencyHash.keys val resolverResults: MutableCollection = ArrayList() @@ -353,6 +358,7 @@ class MavenProjectResolver(private val myProject: Project) { else { resolverResults.add(MavenProjectResolverResult( projectData.mavenModel, + projectData.managedDependencies, projectData.dependencyHash, projectData.dependencyResolutionSkipped, projectData.mavenModelMap, @@ -382,12 +388,14 @@ class MavenProjectResolver(private val myProject: Project) { return null } - private suspend fun collectProjectWithUnresolvedPlugins(result: MavenProjectResolverResult, - artifactIdToMavenProjects: Map>, - generalSettings: MavenGeneralSettings, - embedder: MavenEmbedderWrapper, - tree: MavenProjectsTree, - projectsWithUnresolvedPlugins: ConcurrentLinkedQueue) { + private suspend fun collectProjectWithUnresolvedPlugins( + result: MavenProjectResolverResult, + artifactIdToMavenProjects: Map>, + generalSettings: MavenGeneralSettings, + embedder: MavenEmbedderWrapper, + tree: MavenProjectsTree, + projectsWithUnresolvedPlugins: ConcurrentLinkedQueue, + ) { val mavenId = result.mavenModel.mavenId val artifactId = mavenId.artifactId val mavenProjects = artifactIdToMavenProjects[artifactId] @@ -414,13 +422,14 @@ class MavenProjectResolver(private val myProject: Project) { val keepPreviousArtifacts = keepPreviousResolutionResults || result.dependencyResolutionSkipped MavenLog.LOG.debug( - "Project resolution: updating maven project $mavenProjectCandidate, keepPreviousArtifacts=$keepPreviousArtifacts, dependencies: ${result.mavenModel.dependencies.size}") + "Project resolution: updating maven project $mavenProjectCandidate, keepPreviousArtifacts=$keepPreviousArtifacts, dependencies: ${result.mavenModel.dependencies.size}, managedDeps: ${result.managedDependencies.size}") MavenServerResultTransformer.getInstance(myProject) .transform(result.mavenModel, mavenProjectCandidate.file) mavenProjectCandidate.updateState( result.mavenModel, + result.managedDependencies, result.dependencyHash, result.readingProblems, result.activatedProfiles, @@ -459,9 +468,11 @@ class MavenProjectResolver(private val myProject: Project) { @ApiStatus.ScheduledForRemoval @Deprecated("Use {@link #resolveProject()}") - internal fun resolveProjectSync(embedder: MavenEmbedderWrapper, - files: Collection, - explicitProfiles: MavenExplicitProfiles): Collection { + internal fun resolveProjectSync( + embedder: MavenEmbedderWrapper, + files: Collection, + explicitProfiles: MavenExplicitProfiles, + ): Collection { return runBlockingMaybeCancellable { resolveProjectsInEmbedder( embedder, diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenProjectState.kt b/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenProjectState.kt index 3cffa46b39d8..5332c4821666 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenProjectState.kt +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenProjectState.kt @@ -34,6 +34,7 @@ internal data class MavenProjectState( val remoteRepositories: List = emptyList(), val remotePluginRepositories: List = emptyList(), val annotationProcessors: List = emptyList(), + val managedDependencies: Map = emptyMap(), val modulesPathsAndNames: Map = emptyMap(), val modelMap: Map = emptyMap(), val profilesIds: Collection = emptySet(), diff --git a/plugins/maven/src/test/java/org/jetbrains/idea/maven/project/importing/MavenProjectTest.kt b/plugins/maven/src/test/java/org/jetbrains/idea/maven/project/importing/MavenProjectTest.kt index 72e62e710ee5..d108a9bc26c5 100644 --- a/plugins/maven/src/test/java/org/jetbrains/idea/maven/project/importing/MavenProjectTest.kt +++ b/plugins/maven/src/test/java/org/jetbrains/idea/maven/project/importing/MavenProjectTest.kt @@ -999,6 +999,110 @@ class MavenProjectTest : MavenMultiVersionImportingTestCase() { nodes[1]!!.dependencies[0].relatedArtifact) } + @Test + fun testManagedDependencies() = runBlocking{ + val p = createProjectPom(""" + test + test + 1 + + + + junit + junit + 4.0 + + + +""") + + importProjectAsync() + + assertEquals("4.0", projectsTree.findProject(p)!!.findManagedDependency("junit", "junit")!!.version) + } + + @Test + fun testManagedDependenciesFromParent() = runBlocking{ + val p = createProjectPom(""" + test + test + 1 + pom + + m1 + + + + + junit + junit + 4.0 + + + +""") + + val m1 = createModulePom("m1", + """ + + test + test + 1 + + m1 +""") + + importProjectAsync() + + assertEquals("4.0", projectsTree.findProject(m1)!!.findManagedDependency("junit", "junit")!!.version) + } + + @Test + fun testManagedDependenciesFromParentAndModule() = runBlocking{ + val p = createProjectPom(""" + test + test + 1 + pom + + m1 + + + + + junit + junit + 4.0 + + + +""") + + val m1 = createModulePom("m1", + """ + + test + test + 1 + + m1 + + + + another + dep + 1.0 + + + +""") + + importProjectAsync() + + assertEquals("4.0", projectsTree.findProject(m1)!!.findManagedDependency("junit", "junit")!!.version) + assertEquals("1.0", projectsTree.findProject(m1)!!.findManagedDependency("another", "dep")!!.version) + } + protected fun assertDependenciesNodes(nodes: List?, expected: String?) { assertEquals(expected, StringUtil.join(nodes!!, ",")) }