Skip to content

Commit

Permalink
Handle the dependency update due to spring boot 2.3.0-RELEASE (#4589)
Browse files Browse the repository at this point in the history
* Handle the dependency update due to spring boot 2.3.0-RELEASE

* Update maven project if it is dirty, since idea introduces a new way of Maven and Gradle importing :https://blog.jetbrains.com/idea/2020/01/intellij-idea-2020-1-eap/#maven_and_gradle_importing_updates

Co-authored-by: Andy Xu(devdiv) <andxu@microsoft>
  • Loading branch information
andxu and Andy Xu(devdiv) authored Sep 22, 2020
1 parent e6b76ec commit 2c37251
Show file tree
Hide file tree
Showing 2 changed files with 174 additions and 113 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,16 @@

package com.microsoft.azure.springcloud.dependency.action;

import com.google.common.util.concurrent.SettableFuture;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.CommonDataKeys;
import com.intellij.openapi.actionSystem.LangDataKeys;
import com.intellij.openapi.actionSystem.Presentation;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.EditorKind;
import com.intellij.openapi.externalSystem.autoimport.ExternalSystemProjectTracker;
import com.intellij.openapi.externalSystem.autoimport.ProjectNotificationAware;
import com.intellij.openapi.fileEditor.FileEditorManager;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleTypeId;
Expand Down Expand Up @@ -60,16 +63,15 @@

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.util.concurrent.ExecutionException;

public class AddAzureDependencyAction extends AzureAnAction {
public static final String SPRING_CLOUD_GROUP_ID = "org.springframework.cloud";
public static final String SPRING_BOOT_GROUP_ID = "org.springframework.boot";
private static final String GROUP_ID = "com.microsoft.azure";
private static final String ARTIFACT_ID = "spring-cloud-starter-azure-spring-cloud-client";
private static final String SPRING_CLOUD_COMMONS_KEY = "org.springframework.cloud:spring-cloud-commons";

@Override
public boolean onActionPerformed(@NotNull AnActionEvent event, @Nullable Operation operation) {
Expand All @@ -79,87 +81,83 @@ public boolean onActionPerformed(@NotNull AnActionEvent event, @Nullable Operati
final MavenProject mavenProject = projectsManager.findProject(module);
if (mavenProject == null) {
PluginUtil.showErrorNotificationProject(project, "Error",
String.format("Project '%s' is not a maven project.",
project.getName()));
String.format("Project '%s' is not a maven project.",
project.getName()));
return true;
}

DefaultLoader.getIdeHelper().runInBackground(project, "Deleting Docker Host", false, true, "Update Azure Spring Cloud dependencies", () -> {
ProgressIndicator progressIndicator = ProgressManager.getInstance().getProgressIndicator();
progressIndicator.setText("Syncing maven project " + project.getName());
if (projectsManager.hasScheduledProjects()) {
projectsManager.forceUpdateProjects(Collections.singletonList(mavenProject)).get();
}
try {
progressIndicator.setText("Check existing dependencies");
final String evaluateEffectivePom = MavenUtils.evaluateEffectivePom(project, mavenProject);
ProgressManager.checkCanceled();
if (StringUtils.isEmpty(evaluateEffectivePom)) {
PluginUtil.showErrorNotificationProject(project, "Error", "Failed to evaluate effective pom.");
return;
}
final String springBootVer = getMavenLibraryVersion(mavenProject, SPRING_BOOT_GROUP_ID, "spring-boot-autoconfigure");
if (StringUtils.isEmpty(springBootVer)) {
throw new AzureExecutionException(String.format("Module %s is not a spring-boot application.", module.getName()));
}
progressIndicator.setText("Get latest versions ...");
SpringCloudDependencyManager manager = new SpringCloudDependencyManager(evaluateEffectivePom);
Map<String, DependencyArtifact> versionMaps = manager.getDependencyVersions();
List<DependencyArtifact> dep = new ArrayList<>();
dep.add(getDependencyArtifact(GROUP_ID, ARTIFACT_ID, versionMaps));
dep.add(getDependencyArtifact(SPRING_BOOT_GROUP_ID, "spring-boot-starter-actuator", versionMaps));
dep.add(getDependencyArtifact(SPRING_CLOUD_GROUP_ID, "spring-cloud-config-client", versionMaps));
dep.add(getDependencyArtifact(SPRING_CLOUD_GROUP_ID,
"spring-cloud-starter-netflix-eureka-client",
versionMaps));
dep.add(getDependencyArtifact(SPRING_CLOUD_GROUP_ID, "spring-cloud-starter-zipkin", versionMaps));
dep.add(getDependencyArtifact(SPRING_CLOUD_GROUP_ID, "spring-cloud-starter-sleuth", versionMaps));
ProgressManager.checkCanceled();
List<DependencyArtifact> versionChanges = SpringCloudDependencyManager.getCompatibleVersions(dep, springBootVer);
if (versionChanges.isEmpty()) {
PluginUtil.showInfoNotificationProject(project, "Your project is update-to-date.",
"No updates are needed.");
return;
}
progressIndicator.setText("Applying versions ...");
File pomFile = new File(mavenProject.getFile().getCanonicalPath());
ProgressManager.checkCanceled();
Map<String, DependencyArtifact> managementVersions = manager.getDependencyManagementVersions();
versionChanges.stream().filter(change -> managementVersions.containsKey(change.getKey())).forEach(change -> {
String managementVersion = managementVersions.get(change.getKey()).getCurrentVersion();
if (StringUtils.equals(change.getCompatibleVersion(), managementVersion)
|| SpringCloudDependencyManager.isCompatibleVersion(managementVersion, springBootVer)) {
change.setCompatibleVersion("");
change.setManagementVersion(managementVersion);
DefaultLoader.getIdeHelper().runInBackground(project,
"Update Azure Spring Cloud dependencies",
false,
true,
"Update Azure Spring Cloud dependencies",
() -> {
ProgressIndicator progressIndicator = ProgressManager.getInstance().getProgressIndicator();
progressIndicator.setText("Syncing maven project " + project.getName());
final SettableFuture<Boolean> isDirty = SettableFuture.create();

ApplicationManager.getApplication().invokeAndWait(() -> {
ProjectNotificationAware notificationAware = ProjectNotificationAware.getInstance(project);
isDirty.set(notificationAware.isNotificationVisible());
if (notificationAware.isNotificationVisible()) {
ExternalSystemProjectTracker projectTracker = ExternalSystemProjectTracker.getInstance(project);
projectTracker.scheduleProjectRefresh();
}
});
if (!manager.update(pomFile, versionChanges)) {
PluginUtil.showInfoNotificationProject(project, "Your project is update-to-date.",
"No updates are needed.");
try {
if (isDirty.get().booleanValue()) {
projectsManager.forceUpdateProjects(Collections.singletonList(mavenProject)).get();
}
} catch (InterruptedException | ExecutionException e) {
PluginUtil.showErrorNotification("Error",
"Failed to update project due to error: "
+ e.getMessage());
return;
}

final VirtualFile vf = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(pomFile);
RefreshQueue.getInstance().refresh(true, false, null, new VirtualFile[]{vf});
ApplicationManager.getApplication().invokeLater(() -> {
FileEditorManager.getInstance(project).closeFile(vf);
FileEditorManager.getInstance(project).openFile(vf, true, true);
if (versionChanges.stream().anyMatch(t -> StringUtils.isNotEmpty(t.getCurrentVersion()))) {
PluginUtil.showInfoNotificationProject(project,
"Azure Spring Cloud dependencies are updated successfully.",
summaryVersionChanges(versionChanges));
try {
progressIndicator.setText("Check existing dependencies");
final String evaluateEffectivePom = MavenUtils.evaluateEffectivePom(project, mavenProject);
ProgressManager.checkCanceled();
if (StringUtils.isEmpty(evaluateEffectivePom)) {
PluginUtil.showErrorNotificationProject(project, "Error", "Failed to evaluate effective pom.");
return;
}
final String springBootVer = getMavenLibraryVersion(mavenProject, SPRING_BOOT_GROUP_ID, "spring-boot-autoconfigure");
if (StringUtils.isEmpty(springBootVer)) {
throw new AzureExecutionException(String.format("Module %s is not a spring-boot application.", module.getName()));
}
progressIndicator.setText("Get latest versions ...");
SpringCloudDependencyManager dependencyManager = new SpringCloudDependencyManager(evaluateEffectivePom);
Map<String, DependencyArtifact> versionMaps = dependencyManager.getDependencyVersions();
Map<String, DependencyArtifact> managerDependencyVersionsMaps = dependencyManager.getDependencyManagementVersions();

// given the spring-cloud-commons is greater or equal to 2.2.5.RELEASE, we should not add spring-cloud-starter-azure-spring-cloud-client
// because the code is already merged into spring repo: https://github.com/spring-cloud/spring-cloud-commons/pull/803
boolean noAzureSpringCloudClientDependency = shouldNotAddAzureSpringCloudClientDependency(versionMaps) ||
shouldNotAddAzureSpringCloudClientDependency(managerDependencyVersionsMaps);

ProgressManager.checkCanceled();
final List<DependencyArtifact> versionChanges = calculateVersionChanges(springBootVer, noAzureSpringCloudClientDependency, versionMaps);
if (versionChanges.isEmpty()) {
PluginUtil.showInfoNotificationProject(project, "Your project is update-to-date.",
"No updates are needed.");
return;
}
progressIndicator.setText("Applying versions ...");
final File pomFile = new File(mavenProject.getFile().getCanonicalPath());
ProgressManager.checkCanceled();
if (applyVersionChanges(dependencyManager, pomFile, springBootVer, managerDependencyVersionsMaps, versionChanges)) {
noticeUserVersionChanges(project, pomFile, versionChanges);
} else {
PluginUtil.showInfoNotificationProject(project,
"Azure Spring Cloud dependencies are added to your project successfully.",
summaryVersionChanges(versionChanges));
PluginUtil.showInfoNotificationProject(project, "Your project is update-to-date.", "No updates are needed.");
}
});
} catch (DocumentException | IOException | AzureExecutionException | MavenProcessCanceledException e) {
PluginUtil.showErrorNotification("Error",
"Failed to update Azure Spring Cloud dependencies due to error: "
+ e.getMessage());
}
});
} catch (DocumentException | IOException | AzureExecutionException | MavenProcessCanceledException e) {
PluginUtil.showErrorNotification("Error",
"Failed to update Azure Spring Cloud dependencies due to error: "
+ e.getMessage());
}
});

return false;
}
Expand Down Expand Up @@ -207,12 +205,12 @@ private static String summaryVersionChanges(List<DependencyArtifact> changes) {
for (DependencyArtifact change : changes) {
boolean isUpdate = StringUtils.isNotEmpty(change.getCurrentVersion());
builder.append(String.format("%s dependency: Group: %s, Artifact: %s, Version: %s%s \n",
isUpdate ? "Update" : "Add ",
change.getGroupId(),
change.getArtifactId(),
isUpdate ? (change.getCurrentVersion() + " -> ") : "",
StringUtils.isNotEmpty(change.getCompatibleVersion()) ? change.getCompatibleVersion() :
change.getManagementVersion()));
isUpdate ? "Update" : "Add ",
change.getGroupId(),
change.getArtifactId(),
isUpdate ? (change.getCurrentVersion() + " -> ") : "",
StringUtils.isNotEmpty(change.getCompatibleVersion()) ? change.getCompatibleVersion() :
change.getManagementVersion()));
}
return builder.toString();
}
Expand All @@ -230,4 +228,65 @@ private static String getMavenLibraryVersion(MavenProject project, String groupI
private static boolean isMatch(MavenArtifact lib, String groupId, String artifactId) {
return StringUtils.equals(lib.getArtifactId(), artifactId) && StringUtils.equals(lib.getGroupId(), groupId);
}

private static boolean shouldNotAddAzureSpringCloudClientDependency(Map<String, DependencyArtifact> versionMaps) {
if (versionMaps.containsKey(SPRING_CLOUD_COMMONS_KEY)) {
String version = versionMaps.get(SPRING_CLOUD_COMMONS_KEY).getCurrentVersion();
return SpringCloudDependencyManager.isGreaterOrEqualVersion(version, "2.2.5.RELEASE");
}
return false;
}

private static List<DependencyArtifact> calculateVersionChanges(String springBootVer,
boolean noAzureSpringCloudClientDependency,
Map<String, DependencyArtifact> versionMaps)
throws AzureExecutionException, DocumentException, IOException {
List<DependencyArtifact> dep = new ArrayList<>();
if (!noAzureSpringCloudClientDependency) {
dep.add(getDependencyArtifact(GROUP_ID, ARTIFACT_ID, versionMaps));
}
dep.add(getDependencyArtifact(SPRING_BOOT_GROUP_ID, "spring-boot-starter-actuator", versionMaps));
dep.add(getDependencyArtifact(SPRING_CLOUD_GROUP_ID, "spring-cloud-config-client", versionMaps));
dep.add(getDependencyArtifact(SPRING_CLOUD_GROUP_ID,
"spring-cloud-starter-netflix-eureka-client",
versionMaps));
dep.add(getDependencyArtifact(SPRING_CLOUD_GROUP_ID, "spring-cloud-starter-zipkin", versionMaps));
dep.add(getDependencyArtifact(SPRING_CLOUD_GROUP_ID, "spring-cloud-starter-sleuth", versionMaps));

return SpringCloudDependencyManager.getCompatibleVersions(dep, springBootVer);
}

private static boolean applyVersionChanges(SpringCloudDependencyManager dependencyManager,
File pomFile,
String springBootVer,
Map<String, DependencyArtifact> managerDependencyVersionsMaps,
List<DependencyArtifact> versionChanges) throws IOException, DocumentException {
versionChanges.stream().filter(change -> managerDependencyVersionsMaps.containsKey(change.getKey())).forEach(change -> {
String managementVersion = managerDependencyVersionsMaps.get(change.getKey()).getCurrentVersion();
if (StringUtils.equals(change.getCompatibleVersion(), managementVersion)
|| SpringCloudDependencyManager.isCompatibleVersion(managementVersion, springBootVer)) {
change.setCompatibleVersion("");
change.setManagementVersion(managementVersion);
}
});
return dependencyManager.update(pomFile, versionChanges);
}

private static void noticeUserVersionChanges(Project project, File pomFile, List<DependencyArtifact> versionChanges) {
final VirtualFile vf = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(pomFile);
RefreshQueue.getInstance().refresh(true, false, null, new VirtualFile[]{vf});
ApplicationManager.getApplication().invokeLater(() -> {
FileEditorManager.getInstance(project).closeFile(vf);
FileEditorManager.getInstance(project).openFile(vf, true, true);
if (versionChanges.stream().anyMatch(t -> StringUtils.isNotEmpty(t.getCurrentVersion()))) {
PluginUtil.showInfoNotificationProject(project,
"Azure Spring Cloud dependencies are updated successfully.",
summaryVersionChanges(versionChanges));
} else {
PluginUtil.showInfoNotificationProject(project,
"Azure Spring Cloud dependencies are added to your project successfully.",
summaryVersionChanges(versionChanges));
}
});
}
}
Loading

0 comments on commit 2c37251

Please sign in to comment.