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.*",