From e92323ab740c15775b66fb7a332da661204c7a16 Mon Sep 17 00:00:00 2001 From: aboyko Date: Tue, 24 Jan 2023 17:31:07 -0500 Subject: [PATCH] Project and Version reconcile optimizations Version validation under Project Reconcile architecture Limit initial project reconcile to one per project --- .../app/BootLanguageServerInitializer.java | 52 +++++-- .../boot/app/BootVersionValidationEngine.java | 53 ------- .../BootJavaLanguageServerComponents.java | 3 +- .../handlers/BootJavaReconcileEngine.java | 15 +- .../BootVersionValidationEngine.java} | 33 ++-- .../ProjectVersionDiagnosticProvider.java | 9 +- .../generations/VersionValidator.java | 6 +- .../generations/VersionValidators.java | 145 ++++++++---------- .../AdHocPropertyHarnessTestConf.java | 16 +- .../ValueSpelExpressionValidationTest.java | 2 +- 10 files changed, 139 insertions(+), 195 deletions(-) delete mode 100644 headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/BootVersionValidationEngine.java rename headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/{app/BootVersionValidator.java => validation/BootVersionValidationEngine.java} (72%) diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/BootLanguageServerInitializer.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/BootLanguageServerInitializer.java index c78cc08ff..691751e4b 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/BootLanguageServerInitializer.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/BootLanguageServerInitializer.java @@ -37,6 +37,7 @@ import org.springframework.ide.vscode.boot.java.utils.CompilationUnitCache; import org.springframework.ide.vscode.boot.java.utils.SymbolCache; import org.springframework.ide.vscode.boot.metadata.ProjectBasedPropertyIndexProvider; import org.springframework.ide.vscode.boot.properties.BootPropertiesLanguageServerComponents; +import org.springframework.ide.vscode.boot.validation.BootVersionValidationEngine; import org.springframework.ide.vscode.boot.xml.SpringXMLLanguageServerComponents; import org.springframework.ide.vscode.commons.java.IClasspathUtil; import org.springframework.ide.vscode.commons.java.IJavaProject; @@ -90,7 +91,7 @@ public class BootLanguageServerInitializer implements InitializingBean { private CompositeLanguageServerComponents components; private VscodeCompletionEngineAdapter completionEngineAdapter; - private IJavaProjectReconcileEngine projectReconciler; + private List projectReconcilers; private Scheduler projectReconcileScheduler = Schedulers.newBoundedElastic(1, Integer.MAX_VALUE, "Project-Reconciler", 10); private Map projectReconcileRequests = new ConcurrentHashMap<>(); @@ -142,10 +143,11 @@ public class BootLanguageServerInitializer implements InitializingBean { builder.add(new SpringFactoriesLanguageServerComponents(projectFinder, springIndexer, config)); components = builder.build(server); - projectReconciler = (IJavaProjectReconcileEngine) bootJavaLanguageServerComponent.getReconcileEngine().get(); + projectReconcilers = List.of( + (IJavaProjectReconcileEngine) bootJavaLanguageServerComponent.getReconcileEngine().get(), + new BootVersionValidationEngine(server, config) + ); - params.projectObserver.addListener(reconcileDocumentsForProjectChange(server, components, params.projectFinder)); - SimpleTextDocumentService documents = server.getTextDocumentService(); components.getReconcileEngine().ifPresent(reconcileEngine -> { @@ -171,18 +173,17 @@ public class BootLanguageServerInitializer implements InitializingBean { components.getDocumentSymbolProvider().ifPresent(documents::onDocumentSymbol); - config.addListener(evt -> reconcile()); - if (recipesRepo != null) { - recipesRepo.onRecipesLoaded(v -> reconcile()); + recipesRepo.onRecipesLoaded(v -> { + // Recipes will start loading only after config has been received. Therefore safe to start listening to config changes now + // and launch initial project reconcile since both config and recipes are present + startListeningToPerformReconcile(); + reconcile(); + }); + } else { + startListeningToPerformReconcile(); + reconcile(); } - - server.getWorkspaceService().getFileObserver().onFilesChanged(FILES_TO_WATCH_GLOB, this::handleFiles); - server.getWorkspaceService().getFileObserver().onFilesCreated(FILES_TO_WATCH_GLOB, this::handleFiles); - - // TODO: index update even happens on every file save. Very expensive to blindly reconcile all projects. - // Need to figure out a check if spring index has any changes -// springIndexer.onUpdate(v -> reconcile()); server.onShutdown(() -> { for (IJavaProject p : projectFinder.all()) { @@ -191,6 +192,19 @@ public class BootLanguageServerInitializer implements InitializingBean { }); } + private void startListeningToPerformReconcile() { + config.addListener(evt -> reconcile()); + + params.projectObserver.addListener(reconcileDocumentsForProjectChange(server, components, params.projectFinder)); + + server.getWorkspaceService().getFileObserver().onFilesChanged(FILES_TO_WATCH_GLOB, this::handleFiles); + server.getWorkspaceService().getFileObserver().onFilesCreated(FILES_TO_WATCH_GLOB, this::handleFiles); + + // TODO: index update even happens on every file save. Very expensive to blindly reconcile all projects. + // Need to figure out a check if spring index has any changes +// springIndexer.onUpdate(v -> reconcile()); + } + private void reconcile() { components.getReconcileEngine().ifPresent(reconciler -> { log.info("A configuration changed, triggering reconcile on all open documents"); @@ -226,7 +240,9 @@ public class BootLanguageServerInitializer implements InitializingBean { .doOnSuccess(l -> { if (projectReconcileRequests.remove(uri) != null) { projectFinder.find(new TextDocumentIdentifier(uri.toASCIIString())).ifPresent(p -> { - projectReconciler.reconcile(p, doc -> server.createProblemCollector(doc)); + for (IJavaProjectReconcileEngine projectReconciler : projectReconcilers) { + projectReconciler.reconcile(p, doc -> server.createProblemCollector(doc)); + } }); } }) @@ -250,10 +266,12 @@ public class BootLanguageServerInitializer implements InitializingBean { * which is caused by the #clear(...) call. In the LS reality this will never happen as #publishDiagnsotics() is always a future */ if (asyncClear) { - Mono.fromFuture(CompletableFuture.runAsync(() -> projectReconciler.clear(project))) + Mono.fromFuture(CompletableFuture.runAsync(() -> projectReconcilers.forEach(projectReconciler -> projectReconciler.clear(project)))) .publishOn(projectReconcileScheduler); } else { - projectReconciler.clear(project); + for (IJavaProjectReconcileEngine projectReconciler : projectReconcilers) { + projectReconciler.clear(project); + } } } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/BootVersionValidationEngine.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/BootVersionValidationEngine.java deleted file mode 100644 index 1e02fb19b..000000000 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/BootVersionValidationEngine.java +++ /dev/null @@ -1,53 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2022, 2023 VMware, Inc. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * https://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * VMware, Inc. - initial API and implementation - *******************************************************************************/ -package org.springframework.ide.vscode.boot.app; - -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; - -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass; -import org.springframework.ide.vscode.commons.java.IJavaProject; -import org.springframework.ide.vscode.commons.languageserver.java.ProjectObserver; -import org.springframework.stereotype.Component; - -@Component -@ConditionalOnMissingClass("org.springframework.ide.vscode.languageserver.testharness.LanguageServerHarness") -public class BootVersionValidationEngine { - - private final BootVersionValidator bootVersionValidator; - private final ExecutorService validationExecutor = Executors.newFixedThreadPool(3); - - public BootVersionValidationEngine(ProjectObserver observer, BootVersionValidator bootVersionValidator) { - this.bootVersionValidator = bootVersionValidator; - - observer.addListener(new ProjectObserver.Listener() { - - @Override - public void deleted(IJavaProject project) { - } - - @Override - public void created(IJavaProject project) { - validate(project); - } - - @Override - public void changed(IJavaProject project) { - validate(project); - } - }); - } - - public void validate(IJavaProject project) { - validationExecutor.submit(() -> bootVersionValidator.validate(project)); - } - -} diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/BootJavaLanguageServerComponents.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/BootJavaLanguageServerComponents.java index 7b47cd5f6..2269b89ba 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/BootJavaLanguageServerComponents.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/BootJavaLanguageServerComponents.java @@ -23,7 +23,6 @@ import org.slf4j.LoggerFactory; import org.springframework.context.ApplicationContext; import org.springframework.ide.vscode.boot.app.BootJavaConfig; import org.springframework.ide.vscode.boot.app.BootLanguageServerParams; -import org.springframework.ide.vscode.boot.app.BootVersionValidationEngine; import org.springframework.ide.vscode.boot.app.SpringSymbolIndex; import org.springframework.ide.vscode.boot.java.annotations.AnnotationHierarchyAwareLookup; import org.springframework.ide.vscode.boot.java.autowired.AutowiredHoverProvider; @@ -202,7 +201,7 @@ public class BootJavaLanguageServerComponents implements LanguageServerComponent reconcileEngine = new BootJavaReconcileEngine(projectFinder, new JavaReconciler[] { jdtReconciler, rewriteJavaReconciler - }, documents, appContext.getBean(BootVersionValidationEngine.class)); + }, documents); codeActionProvider = new BootJavaCodeActionProvider( projectFinder, 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 43178d8a1..b14cb921d 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 @@ -25,7 +25,6 @@ import java.util.stream.Stream; import org.eclipse.lsp4j.TextDocumentIdentifier; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.ide.vscode.boot.app.BootVersionValidationEngine; import org.springframework.ide.vscode.boot.common.IJavaProjectReconcileEngine; import org.springframework.ide.vscode.boot.java.reconcilers.JavaReconciler; import org.springframework.ide.vscode.commons.java.IClasspathUtil; @@ -51,13 +50,10 @@ public class BootJavaReconcileEngine implements IReconcileEngine, IJavaProjectRe private final JavaProjectFinder projectFinder; private JavaReconciler[] javaReconcilers; - private BootVersionValidationEngine bootVersionValidationEngine; - - public BootJavaReconcileEngine(JavaProjectFinder projectFinder, JavaReconciler[] javaReconcilers, SimpleTextDocumentService documents, BootVersionValidationEngine bootVersionValidator) { + public BootJavaReconcileEngine(JavaProjectFinder projectFinder, JavaReconciler[] javaReconcilers, SimpleTextDocumentService documents) { this.documents = documents; this.projectFinder = projectFinder; this.javaReconcilers = javaReconcilers; - this.bootVersionValidationEngine = bootVersionValidator; } @Override @@ -118,10 +114,6 @@ public class BootJavaReconcileEngine implements IReconcileEngine, IJavaProjectRe @Override public void reconcile(IJavaProject project, Function problemCollectorFactory) { - if (bootVersionValidationEngine != null) { - bootVersionValidationEngine.validate(project); - } - Stream files = IClasspathUtil.getProjectJavaSourceFolders(project.getClasspath()).flatMap(folder -> { try { return Files.walk(folder.toPath()).filter(Files::isRegularFile); @@ -161,11 +153,6 @@ public class BootJavaReconcileEngine implements IReconcileEngine, IJavaProjectRe @Override public void clear(IJavaProject project) { - // Build file - if (project.getProjectBuild() != null && project.getProjectBuild().getBuildFile() != null) { - documents.publishDiagnostics(new TextDocumentIdentifier(project.getProjectBuild().getBuildFile().toASCIIString()), Collections.emptyList()); - } - // Rest of the files IClasspathUtil.getProjectJavaSourceFolders(project.getClasspath()).flatMap(folder -> { try { return Files.walk(folder.toPath()).filter(Files::isRegularFile); 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/validation/BootVersionValidationEngine.java similarity index 72% rename from headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/BootVersionValidator.java rename to headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/validation/BootVersionValidationEngine.java index de0c8c2c5..db6d8e7bc 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/validation/BootVersionValidationEngine.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2022 VMware, Inc. + * Copyright (c) 2022, 2023 VMware, Inc. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -8,36 +8,41 @@ * Contributors: * VMware, Inc. - initial API and implementation *******************************************************************************/ -package org.springframework.ide.vscode.boot.app; +package org.springframework.ide.vscode.boot.validation; + +import java.util.Collections; +import java.util.function.Function; import org.eclipse.lsp4j.TextDocumentIdentifier; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.ide.vscode.boot.app.BootJavaConfig; +import org.springframework.ide.vscode.boot.common.IJavaProjectReconcileEngine; import org.springframework.ide.vscode.boot.validation.generations.ProjectVersionDiagnosticProvider; +import org.springframework.ide.vscode.boot.validation.generations.ProjectVersionDiagnosticProvider.DiagnosticResult; import org.springframework.ide.vscode.boot.validation.generations.SpringIoProjectsProvider; import org.springframework.ide.vscode.boot.validation.generations.SpringProjectsClient; import org.springframework.ide.vscode.boot.validation.generations.SpringProjectsProvider; import org.springframework.ide.vscode.boot.validation.generations.VersionValidators; -import org.springframework.ide.vscode.boot.validation.generations.ProjectVersionDiagnosticProvider.DiagnosticResult; import org.springframework.ide.vscode.boot.validation.generations.preferences.VersionValidationPreferences; import org.springframework.ide.vscode.commons.java.IJavaProject; +import org.springframework.ide.vscode.commons.languageserver.reconcile.IProblemCollector; import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer; -import org.springframework.stereotype.Component; +import org.springframework.ide.vscode.commons.util.text.TextDocument; -@Component -public class BootVersionValidator { +public class BootVersionValidationEngine implements IJavaProjectReconcileEngine { - private static final Logger log = LoggerFactory.getLogger(BootVersionValidator.class); + private static final Logger log = LoggerFactory.getLogger(BootVersionValidationEngine.class); private SimpleLanguageServer server; private BootJavaConfig config; - public BootVersionValidator(SimpleLanguageServer server, BootJavaConfig config) { + public BootVersionValidationEngine(SimpleLanguageServer server, BootJavaConfig config) { this.server = server; this.config = config; } - public void validate(IJavaProject project) { + public void reconcile(IJavaProject project, Function problemCollectorFactory) { if (config.isBootVersionValidationEnabled()) { log.debug("validating Spring Boot version on project: " + project.getElementName()); long start = System.currentTimeMillis(); @@ -71,4 +76,14 @@ public class BootVersionValidator { private String getSpringProjectsUrl(VersionValidationPreferences preferences) { return preferences.getSpringProjectsUrl(); } + + @Override + public void clear(IJavaProject project) { + // Build file + if (project.getProjectBuild() != null && project.getProjectBuild().getBuildFile() != null) { + server.getTextDocumentService().publishDiagnostics( + new TextDocumentIdentifier(project.getProjectBuild().getBuildFile().toASCIIString()), + Collections.emptyList()); + } + } } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/validation/generations/ProjectVersionDiagnosticProvider.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/validation/generations/ProjectVersionDiagnosticProvider.java index 65c0c2961..e68b24194 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/validation/generations/ProjectVersionDiagnosticProvider.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/validation/generations/ProjectVersionDiagnosticProvider.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2022 VMware, Inc. + * Copyright (c) 2022, 2023 VMware, Inc. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -13,6 +13,7 @@ package org.springframework.ide.vscode.boot.validation.generations; import java.io.File; import java.net.URI; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.List; @@ -52,9 +53,9 @@ public class ProjectVersionDiagnosticProvider { for (VersionValidator validator : validators.getValidators()) { try { - Diagnostic diagnostic = validator.validate(javaProject, javaProjectVersion); - if (diagnostic != null) { - diagnostics.add(diagnostic); + Collection batch = validator.validate(javaProject, javaProjectVersion); + if (batch != null) { + diagnostics.addAll(batch); } } catch (Exception e) { log.error("", e); diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/validation/generations/VersionValidator.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/validation/generations/VersionValidator.java index ab45e9b4a..159bc7f55 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/validation/generations/VersionValidator.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/validation/generations/VersionValidator.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2022 VMware, Inc. + * Copyright (c) 2022, 2023 VMware, Inc. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -10,12 +10,14 @@ *******************************************************************************/ package org.springframework.ide.vscode.boot.validation.generations; +import java.util.Collection; + import org.eclipse.lsp4j.Diagnostic; import org.springframework.ide.vscode.commons.java.IJavaProject; import org.springframework.ide.vscode.commons.java.Version; public interface VersionValidator { - Diagnostic validate(IJavaProject javaProject, Version javaProjectVersion) throws Exception; + Collection validate(IJavaProject javaProject, Version javaProjectVersion) throws Exception; } 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 ce0de1d27..143a39630 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 @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2022 VMware, Inc. + * Copyright (c) 2022, 2023 VMware, Inc. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -11,19 +11,17 @@ package org.springframework.ide.vscode.boot.validation.generations; import java.util.Arrays; +import java.util.Collection; import java.util.List; +import java.util.Optional; -import org.eclipse.core.runtime.Assert; import org.eclipse.lsp4j.CodeAction; import org.eclipse.lsp4j.CodeActionKind; import org.eclipse.lsp4j.Command; import org.eclipse.lsp4j.Diagnostic; import org.springframework.ide.vscode.boot.java.rewrite.SpringBootUpgrade; -import org.springframework.ide.vscode.boot.validation.generations.json.Generation; -import org.springframework.ide.vscode.boot.validation.generations.json.ResolvedSpringProject; import org.springframework.ide.vscode.boot.validation.generations.preferences.VersionValidationProblemType; import org.springframework.ide.vscode.commons.java.IJavaProject; -import org.springframework.ide.vscode.commons.java.SpringProjectUtil; import org.springframework.ide.vscode.commons.java.Version; import org.springframework.ide.vscode.commons.languageserver.reconcile.DiagnosticSeverityProvider; @@ -41,9 +39,8 @@ public class VersionValidators { // new UnsupportedCommercialValidator(diagnosticSeverityProvider, provider), // new UnsupportedOssValidator(diagnosticSeverityProvider, provider), // new SupportedCommercialValidator(diagnosticSeverityProvider, provider), - new UpdateLatestMajorVersion(diagnosticSeverityProvider), - new UpdateLatestMinorVersion(diagnosticSeverityProvider), - new UpdateLatestPatchVersion(diagnosticSeverityProvider) }; + new UpdateBootVersion(diagnosticSeverityProvider) + }; } public List getValidators() { @@ -180,77 +177,25 @@ public class VersionValidators { // } // } - private static class UpdateLatestPatchVersion extends AbstractDiagnosticValidator { + + private static class UpdateBootVersion extends AbstractDiagnosticValidator { - public UpdateLatestPatchVersion(DiagnosticSeverityProvider diagnosticSeverityProvider) { + public UpdateBootVersion(DiagnosticSeverityProvider diagnosticSeverityProvider) { super(diagnosticSeverityProvider); } @Override - public Diagnostic validate(IJavaProject javaProject, Version javaProjectVersion) throws Exception { - Version latest = VersionValidationUtils.getNewerLatestPatchRelease(CachedBootVersionsFromMavenCentral.getBootVersions(), javaProjectVersion); - - if (latest != null) { - VersionValidationProblemType problemType = VersionValidationProblemType.UPDATE_LATEST_PATCH_VERSION; - - StringBuffer message = new StringBuffer(); - message.append("Newer patch version of Spring Boot available: "); - message.append(latest.toString()); - - CodeAction ca = new CodeAction(); - ca.setKind(CodeActionKind.QuickFix); - ca.setTitle("Upgrade to Spring Boot " + latest.toString() + " (Maven dependency version changes only)"); - String commandId = SpringBootUpgrade.CMD_UPGRADE_SPRING_BOOT; - ca.setCommand(new Command("Upgrade to Version " + latest.toString(), commandId, - ImmutableList.of(javaProject.getLocationUri().toASCIIString(), latest.toString()))); - - - return createDiagnostic(ca, problemType, message.toString()); - } - return null; + public Collection validate(IJavaProject javaProject, Version javaProjectVersion) throws Exception { + List versions = CachedBootVersionsFromMavenCentral.getBootVersions(); + ImmutableList.Builder builder = ImmutableList.builder(); + validateMajorVersion(javaProject, javaProjectVersion, versions).ifPresent(builder::add); + validateMinorVersion(javaProject, javaProjectVersion, versions).ifPresent(builder::add); + validatePatchVersion(javaProject, javaProjectVersion, versions).ifPresent(builder::add); + return builder.build(); } - } - - private static class UpdateLatestMinorVersion extends AbstractDiagnosticValidator { - - public UpdateLatestMinorVersion(DiagnosticSeverityProvider diagnosticSeverityProvider) { - super(diagnosticSeverityProvider); - } - - @Override - public Diagnostic validate(IJavaProject javaProject, Version javaProjectVersion) throws Exception { - Version latest = VersionValidationUtils.getNewerLatestMinorRelease(CachedBootVersionsFromMavenCentral.getBootVersions(), javaProjectVersion); - - if (latest != null) { - VersionValidationProblemType problemType = VersionValidationProblemType.UPDATE_LATEST_MINOR_VERSION; - - StringBuffer message = new StringBuffer(); - message.append("Newer minor version of Spring Boot available: "); - message.append(latest.toString()); - - CodeAction ca = new CodeAction(); - ca.setKind(CodeActionKind.QuickFix); - ca.setTitle("Upgrade to Spring Boot " + latest.toString() + " (executes the full project conversion recipe from OpenRewrite)"); - String commandId = SpringBootUpgrade.CMD_UPGRADE_SPRING_BOOT; - ca.setCommand(new Command("Upgrade to Version " + latest.toString(), commandId, - ImmutableList.of(javaProject.getLocationUri().toASCIIString(), latest.toString()))); - - - return createDiagnostic(ca, problemType, message.toString()); - } - return null; - } - } - - private static class UpdateLatestMajorVersion extends AbstractDiagnosticValidator { - - public UpdateLatestMajorVersion(DiagnosticSeverityProvider diagnosticSeverityProvider) { - super(diagnosticSeverityProvider); - } - - @Override - public Diagnostic validate(IJavaProject javaProject, Version javaProjectVersion) throws Exception { - Version latest = VersionValidationUtils.getNewerLatestMajorRelease(CachedBootVersionsFromMavenCentral.getBootVersions(), javaProjectVersion); + + private Optional validateMajorVersion(IJavaProject javaProject, Version javaProjectVersion, List sortedBootVersions) { + Version latest = VersionValidationUtils.getNewerLatestMajorRelease(sortedBootVersions, javaProjectVersion); if (latest != null) { VersionValidationProblemType problemType = VersionValidationProblemType.UPDATE_LATEST_MAJOR_VERSION; @@ -267,12 +212,56 @@ public class VersionValidators { ImmutableList.of(javaProject.getLocationUri().toASCIIString(), latest.toString()))); - return createDiagnostic(ca, problemType, message.toString()); + return Optional.of(createDiagnostic(ca, problemType, message.toString())); } - return null; + return Optional.empty(); + } + + private Optional validateMinorVersion(IJavaProject javaProject, Version javaProjectVersion, List sortedBootVersions) { + Version latest = VersionValidationUtils.getNewerLatestMinorRelease(sortedBootVersions, javaProjectVersion); + + if (latest != null) { + VersionValidationProblemType problemType = VersionValidationProblemType.UPDATE_LATEST_MINOR_VERSION; + + StringBuffer message = new StringBuffer(); + message.append("Newer minor version of Spring Boot available: "); + message.append(latest.toString()); + + CodeAction ca = new CodeAction(); + ca.setKind(CodeActionKind.QuickFix); + ca.setTitle("Upgrade to Spring Boot " + latest.toString() + " (executes the full project conversion recipe from OpenRewrite)"); + String commandId = SpringBootUpgrade.CMD_UPGRADE_SPRING_BOOT; + ca.setCommand(new Command("Upgrade to Version " + latest.toString(), commandId, + ImmutableList.of(javaProject.getLocationUri().toASCIIString(), latest.toString()))); + + + return Optional.of(createDiagnostic(ca, problemType, message.toString())); + } + return Optional.empty(); + } + + private Optional validatePatchVersion(IJavaProject javaProject, Version javaProjectVersion, List sortedBootVersions) { + Version latest = VersionValidationUtils.getNewerLatestPatchRelease(sortedBootVersions, javaProjectVersion); + + if (latest != null) { + VersionValidationProblemType problemType = VersionValidationProblemType.UPDATE_LATEST_PATCH_VERSION; + + StringBuffer message = new StringBuffer(); + message.append("Newer patch version of Spring Boot available: "); + message.append(latest.toString()); + + CodeAction ca = new CodeAction(); + ca.setKind(CodeActionKind.QuickFix); + ca.setTitle("Upgrade to Spring Boot " + latest.toString() + " (Maven dependency version changes only)"); + String commandId = SpringBootUpgrade.CMD_UPGRADE_SPRING_BOOT; + ca.setCommand(new Command("Upgrade to Version " + latest.toString(), commandId, + ImmutableList.of(javaProject.getLocationUri().toASCIIString(), latest.toString()))); + + + return Optional.of(createDiagnostic(ca, problemType, message.toString())); + } + return Optional.empty(); } } - - - } + diff --git a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/bootiful/AdHocPropertyHarnessTestConf.java b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/bootiful/AdHocPropertyHarnessTestConf.java index ce2c4d6d8..f98c7d56a 100644 --- a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/bootiful/AdHocPropertyHarnessTestConf.java +++ b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/bootiful/AdHocPropertyHarnessTestConf.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2018 Pivotal, Inc. + * Copyright (c) 2018, 2023 Pivotal, Inc. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -12,11 +12,8 @@ package org.springframework.ide.vscode.boot.bootiful; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.ide.vscode.boot.app.BootVersionValidationEngine; import org.springframework.ide.vscode.boot.editor.harness.AdHocPropertyHarness; -import org.springframework.ide.vscode.boot.java.utils.test.MockProjectObserver; import org.springframework.ide.vscode.boot.metadata.ProjectBasedPropertyIndexProvider; -import org.springframework.ide.vscode.commons.java.IJavaProject; @Configuration public class AdHocPropertyHarnessTestConf { @@ -28,15 +25,4 @@ public class AdHocPropertyHarnessTestConf { return adHocProperties.getIndexProvider(); } - @Bean BootVersionValidationEngine versionValidator() { - return new BootVersionValidationEngine(new MockProjectObserver(), null) { - - @Override - public void validate(IJavaProject project) { - // do not validate anything - } - - }; - } - } diff --git a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/value/test/ValueSpelExpressionValidationTest.java b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/value/test/ValueSpelExpressionValidationTest.java index aaa1a9746..b7de53ec4 100644 --- a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/value/test/ValueSpelExpressionValidationTest.java +++ b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/value/test/ValueSpelExpressionValidationTest.java @@ -149,7 +149,7 @@ public class ValueSpelExpressionValidationTest { problemCollector = new TestProblemCollector(); reconcileEngine = new BootJavaReconcileEngine(projectFinder, new JavaReconciler[] { new JdtReconciler(compilationUnitCache, config) - }, server.getTextDocumentService(), null); + }, server.getTextDocumentService()); } @AfterEach