From 09c62da4147dd5d713525cccd3509c1954935dc8 Mon Sep 17 00:00:00 2001 From: aboyko Date: Wed, 16 Nov 2022 17:05:07 -0500 Subject: [PATCH] Upgrade to latest patch. CodeAction list rather than single and more 1. CodeAction array rather than single code action expected 2. Support upgrade to the latest patch version 3. Boot version validation async from java sources validation 4. Labels for boot version quick fixes --- .../tooling/boot/ls/PrefsInitializer.java | 1 + .../commons/java/SpringProjectUtil.java | 18 ++++++++++ .../ide/vscode/boot/app/BootJavaConfig.java | 11 +++++- .../vscode/boot/app/BootVersionValidator.java | 36 ++++++++++--------- .../handlers/BootJavaReconcileEngine.java | 9 ++++- .../boot/java/rewrite/SpringBootUpgrade.java | 7 ++-- .../AbstractDiagnosticValidator.java | 2 +- .../generations/VersionValidators.java | 4 +-- .../java/rewrite/SpringBootUpgradeTest.java | 2 ++ .../ProjectGenerationsValidationTest.java | 19 ++++++++-- .../vscode-spring-boot/package.json | 1 + 11 files changed, 83 insertions(+), 27 deletions(-) diff --git a/eclipse-language-servers/org.springframework.tooling.boot.ls/src/org/springframework/tooling/boot/ls/PrefsInitializer.java b/eclipse-language-servers/org.springframework.tooling.boot.ls/src/org/springframework/tooling/boot/ls/PrefsInitializer.java index 97eefb4ce..4ffd32593 100644 --- a/eclipse-language-servers/org.springframework.tooling.boot.ls/src/org/springframework/tooling/boot/ls/PrefsInitializer.java +++ b/eclipse-language-servers/org.springframework.tooling.boot.ls/src/org/springframework/tooling/boot/ls/PrefsInitializer.java @@ -52,6 +52,7 @@ public class PrefsInitializer extends AbstractPreferenceInitializer { "org.openrewrite.java.testing.junit5.JUnit5BestPractices", "org.openrewrite.java.testing.junit5.JUnit4to5Migration", "org.openrewrite.java.spring.boot2.UpgradeSpringBoot_2_6", + "org.openrewrite.java.spring.boot2.UpgradeSpringBoot_2_7", "org.springframework.sts.java.spring.boot3.UpgradeSpringBoot_3_0", "org.rewrite.java.security.*", "org.springframework.rewrite.test.*", diff --git a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/SpringProjectUtil.java b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/SpringProjectUtil.java index c2d4ba3cf..ce9645440 100644 --- a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/SpringProjectUtil.java +++ b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/SpringProjectUtil.java @@ -200,6 +200,24 @@ public class SpringProjectUtil { Integer.parseInt(patch), qualifier ); + } else { + String[] tokens = version.split("\\."); + if (tokens.length <= 3) { + if (tokens.length >= 1) { + int major = Integer.parseInt(tokens[0]); + if (tokens.length >= 2) { + int minor = Integer.parseInt(tokens[1]); + if (tokens.length == 3) { + int patch = Integer.parseInt(tokens[2]); + return new Version(major, minor, patch, null); + } else { + return new Version(major, minor, 0, null); + } + } else { + return new Version(major, 0, 0, null); + } + } + } } return null; } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/BootJavaConfig.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/BootJavaConfig.java index e6c2d360c..6e4a66e10 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/BootJavaConfig.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/BootJavaConfig.java @@ -119,6 +119,15 @@ public class BootJavaConfig implements InitializingBean { public boolean isSpelExpressionValidationEnabled() { Toggle categorySwitch = SpringProblemCategories.SPEL.getToggle(); + return isProblemCategoryEnabled(categorySwitch); + } + + public boolean isBootVersionValidationEnabled() { + Toggle categorySwitch = SpringProblemCategories.VERSION_VALIDATION.getToggle(); + return isProblemCategoryEnabled(categorySwitch); + } + + private boolean isProblemCategoryEnabled(Toggle categorySwitch) { String enabled = settings.getString(categorySwitch.getPreferenceKey().split("\\.")); if (enabled == null) { return categorySwitch.getDefaultValue() == Toggle.Option.ON; @@ -133,7 +142,7 @@ public class BootJavaConfig implements InitializingBean { } } } - + public boolean areXmlHyperlinksEnabled() { Boolean enabled = settings.getBoolean("boot-java", "support-spring-xml-config", "hyperlinks"); return enabled != null && enabled.booleanValue(); diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/BootVersionValidator.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/BootVersionValidator.java index 10ad95a41..8c458dcf8 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/BootVersionValidator.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/BootVersionValidator.java @@ -30,9 +30,11 @@ public class BootVersionValidator { private static final Logger log = LoggerFactory.getLogger(BootVersionValidator.class); private SimpleLanguageServer server; + private BootJavaConfig config; - public BootVersionValidator(SimpleLanguageServer server, ProjectObserver observer) { + public BootVersionValidator(SimpleLanguageServer server, ProjectObserver observer, BootJavaConfig config) { this.server = server; + this.config = config; observer.addListener(new ProjectObserver.Listener() { @Override @@ -52,26 +54,28 @@ public class BootVersionValidator { } public void validate(IJavaProject project) { - VersionValidationPreferences preferences = new VersionValidationPreferences(); + if (config.isBootVersionValidationEnabled()) { + VersionValidationPreferences preferences = new VersionValidationPreferences(); - String url = getSpringProjectsUrl(preferences); - SpringProjectsClient client = new SpringProjectsClient(url); - SpringProjectsProvider provider = new SpringIoProjectsProvider(client); - VersionValidators validators = new VersionValidators(server.getDiagnosticSeverityProvider()); + String url = getSpringProjectsUrl(preferences); + SpringProjectsClient client = new SpringProjectsClient(url); + SpringProjectsProvider provider = new SpringIoProjectsProvider(client); + VersionValidators validators = new VersionValidators(server.getDiagnosticSeverityProvider()); - ProjectVersionDiagnosticProvider diagnosticProvider = new ProjectVersionDiagnosticProvider(provider, - validators); + ProjectVersionDiagnosticProvider diagnosticProvider = new ProjectVersionDiagnosticProvider(provider, + validators); - try { - DiagnosticResult result = diagnosticProvider.getDiagnostics(project); - if (result != null && !result.getDiagnostics().isEmpty()) { - server.getTextDocumentService().publishDiagnostics( - new TextDocumentIdentifier(result.getDocumentUri().toString()), - result.getDiagnostics()); + try { + DiagnosticResult result = diagnosticProvider.getDiagnostics(project); + if (result != null && !result.getDiagnostics().isEmpty()) { + server.getTextDocumentService().publishDiagnostics( + new TextDocumentIdentifier(result.getDocumentUri().toString()), + result.getDiagnostics()); + } + } catch (Exception e) { + log.error("Failed validating Spring Project version", e); } - } catch (Exception e) { - log.error("Failed validating Spring Project version", e); } } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/BootJavaReconcileEngine.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/BootJavaReconcileEngine.java index 0c1405e78..28fabaff8 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/BootJavaReconcileEngine.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/BootJavaReconcileEngine.java @@ -16,6 +16,7 @@ import java.nio.file.Path; import java.util.Collection; import java.util.List; import java.util.Map; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Function; import java.util.stream.Collectors; @@ -39,12 +40,18 @@ import org.springframework.ide.vscode.commons.util.text.LanguageId; import org.springframework.ide.vscode.commons.util.text.LazyTextDocument; import org.springframework.ide.vscode.commons.util.text.TextDocument; +import reactor.core.publisher.Mono; +import reactor.core.scheduler.Scheduler; +import reactor.core.scheduler.Schedulers; + /** * @author Martin Lippert */ public class BootJavaReconcileEngine implements IReconcileEngine, IJavaProjectReconcileEngine { private static final Logger log = LoggerFactory.getLogger(BootJavaReconcileEngine.class); + private Scheduler bootVersionValidationScheduler = Schedulers.newBoundedElastic(3, Integer.MAX_VALUE, "Boot-Version-Validation", 10); + private final SimpleTextDocumentService documents; private final JavaProjectFinder projectFinder; @@ -118,7 +125,7 @@ public class BootJavaReconcileEngine implements IReconcileEngine, IJavaProjectRe @Override public void reconcile(IJavaProject project, Function problemCollectorFactory) { if (bootVersionValidator != null) { - bootVersionValidator.validate(project); + Mono.fromFuture(CompletableFuture.runAsync(() -> bootVersionValidator.validate(project))).publishOn(bootVersionValidationScheduler).subscribe(); } Stream files = IClasspathUtil.getProjectJavaSourceFolders(project.getClasspath()).flatMap(folder -> { try { diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/SpringBootUpgrade.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/SpringBootUpgrade.java index 86490d2e0..aadbed246 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/SpringBootUpgrade.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/SpringBootUpgrade.java @@ -63,10 +63,9 @@ public class SpringBootUpgrade { // Version upgrade is not supposed to work for patch version. Only for the major and minor versions. Assert.isLegal( - version.getMajor() < targetVersion.getMajor() || (version.getMajor() == targetVersion.getMajor() - && version.getMinor() < targetVersion.getMinor()), + version.compareTo(targetVersion) < 0, "Cannot upgrade Spring Boot Project '" + project.getElementName() + "' because its version '" - + version.toMajorMinorVersionStr() + "' is newer than target version '" + + version.toMajorMinorVersionStr() + "' is newer or same as the target version '" + targetVersion.toMajorMinorVersionStr() + "'"); return recipeRepo.loaded.thenComposeAsync(loade -> recipeRepo.apply( @@ -79,7 +78,7 @@ public class SpringBootUpgrade { static List createRecipeIdsChain(int major, int minor, int targetMajor, int targetMinor) { List ids = new ArrayList<>(); - for (int currentMajor = major, currentMinor = minor + 1; targetMajor > currentMajor || (targetMajor == currentMajor && currentMinor <= targetMinor);) { + for (int currentMajor = major, currentMinor = minor; targetMajor > currentMajor || (targetMajor == currentMajor && currentMinor <= targetMinor);) { String recipeId = VERSION_TO_RECIPE_ID.get(createVersionString(currentMajor, currentMinor)); if (recipeId == null) { currentMajor++; diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/validation/generations/AbstractDiagnosticValidator.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/validation/generations/AbstractDiagnosticValidator.java index 754ec57f8..7573f2f84 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/validation/generations/AbstractDiagnosticValidator.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/validation/generations/AbstractDiagnosticValidator.java @@ -53,7 +53,7 @@ abstract public class AbstractDiagnosticValidator implements VersionValidator { Diagnostic refDiagnostic = new Diagnostic(diagnostic.getRange(), diagnostic.getMessage(), diagnostic.getSeverity(), diagnostic.getSource()); action.setDiagnostics(List.of(refDiagnostic)); - diagnostic.setData(action); + diagnostic.setData(List.of(action)); } return diagnostic; } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/validation/generations/VersionValidators.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/validation/generations/VersionValidators.java index c16b8b176..986104f6d 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/validation/generations/VersionValidators.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/validation/generations/VersionValidators.java @@ -157,9 +157,9 @@ public class VersionValidators { CodeAction ca = new CodeAction(); ca.setKind(CodeActionKind.QuickFix); - ca.setTitle("Upgrade To Target Version"); + ca.setTitle("Upgrade to Version " + latest.toString()); String commandId = SpringBootUpgrade.CMD_UPGRADE_SPRING_BOOT; - ca.setCommand(new Command("Upgrade To Target Version", commandId, + ca.setCommand(new Command("Upgrade to Version " + latest.toString(), commandId, ImmutableList.of(javaProject.getLocationUri().toString(), latest.toString()))); diff --git a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/rewrite/SpringBootUpgradeTest.java b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/rewrite/SpringBootUpgradeTest.java index 07d949f7f..d6adba5e2 100644 --- a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/rewrite/SpringBootUpgradeTest.java +++ b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/rewrite/SpringBootUpgradeTest.java @@ -33,6 +33,7 @@ public class SpringBootUpgradeTest { @Test public void recipeIdChain2() throws Exception { assertEquals(List.of( + "org.openrewrite.java.spring.boot2.UpgradeSpringBoot_2_2", "org.openrewrite.java.spring.boot2.UpgradeSpringBoot_2_3", "org.openrewrite.java.spring.boot2.UpgradeSpringBoot_2_4", "org.openrewrite.java.spring.boot2.UpgradeSpringBoot_2_5", @@ -59,6 +60,7 @@ public class SpringBootUpgradeTest { @Test public void recipeIdChain4() throws Exception { assertEquals(List.of( + "org.openrewrite.java.spring.boot2.UpgradeSpringBoot_2_2" ), SpringBootUpgrade.createRecipeIdsChain(2, 2, 2, 2)); } diff --git a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/validation/test/ProjectGenerationsValidationTest.java b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/validation/test/ProjectGenerationsValidationTest.java index c1b0ca4f5..5e996fd6f 100644 --- a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/validation/test/ProjectGenerationsValidationTest.java +++ b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/validation/test/ProjectGenerationsValidationTest.java @@ -132,7 +132,7 @@ public class ProjectGenerationsValidationTest { } @Test - public void testVersionCalculation() throws Exception { + public void testVersionCalculation1() throws Exception { Version version = SpringProjectUtil.getVersion("2.7.5"); assertEquals(2, version.getMajor()); assertEquals(7, version.getMinor()); @@ -150,6 +150,21 @@ public class ProjectGenerationsValidationTest { assertEquals(2, version.getMajor()); assertEquals(6, version.getMinor()); assertEquals(14, version.getPatch()); - assertEquals(version.getQualifier(), "RC2"); + assertEquals(version.getQualifier(), "RC2"); + } + + @Test + public void testVersionCalculation2() throws Exception { + Version version = SpringProjectUtil.getVersion("2.7"); + assertEquals(2, version.getMajor()); + assertEquals(7, version.getMinor()); + assertEquals(0, version.getPatch()); + assertNull(version.getQualifier()); + + version = SpringProjectUtil.getVersion("2"); + assertEquals(2, version.getMajor()); + assertEquals(0, version.getMinor()); + assertEquals(0, version.getPatch()); + assertNull(version.getQualifier()); } } diff --git a/vscode-extensions/vscode-spring-boot/package.json b/vscode-extensions/vscode-spring-boot/package.json index d06eb6e95..5df4a3049 100644 --- a/vscode-extensions/vscode-spring-boot/package.json +++ b/vscode-extensions/vscode-spring-boot/package.json @@ -218,6 +218,7 @@ "org.openrewrite.java.testing.junit5.JUnit5BestPractices", "org.openrewrite.java.testing.junit5.JUnit4to5Migration", "org.openrewrite.java.spring.boot2.UpgradeSpringBoot_2_6", + "org.openrewrite.java.spring.boot2.UpgradeSpringBoot_2_7", "org.springframework.sts.java.spring.boot3.UpgradeSpringBoot_3_0", "org.rewrite.java.security.*", "org.springframework.rewrite.test.*",