BootUpgrade quickfix improvements. Springify version validation.

This commit is contained in:
aboyko
2023-01-26 21:58:45 -05:00
parent 13536a725b
commit f44e5f653d
13 changed files with 329 additions and 227 deletions

View File

@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2016, 2022 VMware Inc.
* Copyright (c) 2016, 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
@@ -652,8 +652,6 @@ public final class SimpleLanguageServer implements Sts4LanguageServer, LanguageC
return workspace;
}
private DiagnosticSeverityProvider severityProvider = DiagnosticSeverityProvider.DEFAULT;
/**
* Keeps track of reconcile requests that have been requested but not yet started.
* This is used to more efficiently deal with situation where many requests are fired
@@ -783,6 +781,7 @@ public final class SimpleLanguageServer implements Sts4LanguageServer, LanguageC
@Override
public void accept(ReconcileProblem problem) {
DiagnosticSeverityProvider severityProvider = getDiagnosticSeverityProvider();
try {
DiagnosticSeverity severity = severityProvider.getDiagnosticSeverity(problem);
@@ -855,7 +854,7 @@ public final class SimpleLanguageServer implements Sts4LanguageServer, LanguageC
}
public DiagnosticSeverityProvider getDiagnosticSeverityProvider() {
return severityProvider;
return appContext.getBean(DiagnosticSeverityProvider.class);
}
@Override
@@ -905,10 +904,6 @@ public final class SimpleLanguageServer implements Sts4LanguageServer, LanguageC
return classpathListenerManager.addClasspathListener(classpathListener);
}
public void setDiagnosticSeverityProvider(DiagnosticSeverityProvider severities) {
this.severityProvider = severities;
}
public void setCompletionFilter(Optional<CompletionFilter> completionFilter) {
this.completionFilter = completionFilter;
}

View File

@@ -46,13 +46,11 @@ public class LanguageServerAutoConf {
@Bean(destroyMethod = "")
public SimpleLanguageServer languageServer(
LanguageServerProperties props,
Optional<DiagnosticSeverityProvider> severities,
Optional<CompletionFilter> completionFilter,
ApplicationContext appContext
) throws Exception {
SimpleLanguageServer server = new SimpleLanguageServer(props.getExtensionId(), appContext, props);
server.setCompletionFilter(completionFilter);
severities.ifPresent(server::setDiagnosticSeverityProvider);
return server;
}
@@ -112,4 +110,10 @@ public class LanguageServerAutoConf {
};
}
@ConditionalOnMissingBean(DiagnosticSeverityProvider.class)
@Bean
DiagnosticSeverityProvider defaultDiagnosticSeverityProvider() {
return DiagnosticSeverityProvider.DEFAULT;
}
}

View File

@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2020 Pivotal, Inc.
* Copyright (c) 2020, 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
@@ -13,36 +13,32 @@ package org.springframework.ide.vscode.boot.app;
import java.util.HashMap;
import java.util.Map;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.eclipse.lsp4j.DiagnosticSeverity;
import org.springframework.ide.vscode.commons.languageserver.reconcile.DiagnosticSeverityProvider;
import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemSeverity;
import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemType;
import org.springframework.ide.vscode.commons.languageserver.util.Settings;
import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer;
import org.springframework.ide.vscode.commons.util.Assert;
import org.springframework.stereotype.Component;
@Component
public class ProblemSeverityConfigurer implements InitializingBean {
public class BootDiagnosticSeverityProvider implements DiagnosticSeverityProvider {
@Autowired
private SimpleLanguageServer server;
@Autowired
private BootJavaConfig config;
@Override
public void afterPropertiesSet() throws Exception {
private Map<String, ProblemSeverity> severityOverrides;
public BootDiagnosticSeverityProvider(BootJavaConfig config) {
this.config = config;
config.addListener((x) -> configChanged());
configChanged();
}
private void configChanged() {
private synchronized void configChanged() {
Settings settings = config.getRawSettings();
settings = settings.navigate("spring-boot", "ls", "problem");
Map<String, ProblemSeverity> severityOverrides = new HashMap<>();
severityOverrides = new HashMap<>();
for (String editorType : settings.keys()) {
Settings problemConf = settings.navigate(editorType);
for (String code : problemConf.keys()) {
@@ -51,12 +47,14 @@ public class ProblemSeverityConfigurer implements InitializingBean {
severityOverrides.put(code, ProblemSeverity.valueOf(severity));
}
}
server.setDiagnosticSeverityProvider((ProblemType problem) -> {
ProblemSeverity severity = severityOverrides.get(problem.getCode());
if (severity==null) {
severity = problem.getDefaultSeverity();
}
return DiagnosticSeverityProvider.diagnosticSeverity(severity);
});
}
@Override
public synchronized DiagnosticSeverity getDiagnosticSeverity(ProblemType problem) {
ProblemSeverity severity = severityOverrides.get(problem.getCode());
if (severity==null) {
severity = problem.getDefaultSeverity();
}
return DiagnosticSeverityProvider.diagnosticSeverity(severity);
}
}

View File

@@ -26,8 +26,6 @@ import org.eclipse.lsp4j.CodeActionKind;
import org.eclipse.lsp4j.CodeActionOptions;
import org.eclipse.lsp4j.InitializeParams;
import org.eclipse.lsp4j.ServerCapabilities;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.SpringApplication;
@@ -40,7 +38,6 @@ import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoCon
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.ide.vscode.boot.common.ProjectReconcileScheduler;
import org.springframework.ide.vscode.boot.common.PropertyCompletionFactory;
import org.springframework.ide.vscode.boot.common.RelaxedNameConfig;
import org.springframework.ide.vscode.boot.java.handlers.BootJavaCodeActionProvider;
@@ -75,11 +72,9 @@ import org.springframework.ide.vscode.boot.metadata.ProjectBasedPropertyIndexPro
import org.springframework.ide.vscode.boot.metadata.SpringPropertyIndex;
import org.springframework.ide.vscode.boot.metadata.ValueProviderRegistry;
import org.springframework.ide.vscode.boot.properties.completions.SpringPropertiesCompletionEngine;
import org.springframework.ide.vscode.boot.validation.BootVersionValidationEngine;
import org.springframework.ide.vscode.boot.xml.SpringXMLCompletionEngine;
import org.springframework.ide.vscode.boot.yaml.completions.ApplicationYamlAssistContext;
import org.springframework.ide.vscode.boot.yaml.completions.SpringYamlCompletionEngine;
import org.springframework.ide.vscode.commons.java.IJavaProject;
import org.springframework.ide.vscode.commons.languageserver.LanguageServerRunner;
import org.springframework.ide.vscode.commons.languageserver.java.FutureProjectFinder;
import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder;
@@ -129,8 +124,6 @@ public class BootLanguageServerBootApp {
private static final String SERVER_NAME = "boot-language-server";
private static final Logger log = LoggerFactory.getLogger(BootLanguageServerBootApp.class);
public static void main(String[] args) throws Exception {
Hooks.onOperatorDebug();
System.setProperty(LanguageServerRunner.SYSPROP_LANGUAGESERVER_NAME, SERVER_NAME); //makes it easy to recognize language server processes - and set this as early as possible
@@ -368,37 +361,4 @@ public class BootLanguageServerBootApp {
recipeRepoOpt.orElse(null), projectFinder, server);
}
@ConditionalOnMissingClass("org.springframework.ide.vscode.languageserver.testharness.LanguageServerHarness")
@ConditionalOnProperty(prefix = "languageserver", name = "reconcile-only-opened-docs", havingValue = "false", matchIfMissing = true)
@Bean
ProjectReconcileScheduler bootVersionValidationScheduler(SimpleLanguageServer server, JavaProjectFinder projectFinder, BootJavaConfig config, ProjectObserver projectObserver) {
return new ProjectReconcileScheduler(server, new BootVersionValidationEngine(server, config, projectObserver, projectFinder), projectFinder) {
@Override
protected void init() {
super.init();
config.addListener(evt -> scheduleValidationForAllProjects());
projectObserver.addListener(new ProjectObserver.Listener() {
@Override
public void deleted(IJavaProject project) {
unscheduleValidation(project);
clear(project, true);
}
@Override
public void created(IJavaProject project) {
scheduleValidation(project);
}
@Override
public void changed(IJavaProject project) {
scheduleValidation(project);
}
});
log.info("Started Boot Version reconciler");
}
};
}
}

View File

@@ -0,0 +1,85 @@
/*******************************************************************************
* Copyright (c) 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.List;
import java.util.Optional;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.ide.vscode.boot.common.ProjectReconcileScheduler;
import org.springframework.ide.vscode.boot.java.rewrite.SpringBootUpgrade;
import org.springframework.ide.vscode.boot.validation.BootVersionValidationEngine;
import org.springframework.ide.vscode.boot.validation.generations.ProjectVersionDiagnosticProvider;
import org.springframework.ide.vscode.boot.validation.generations.UpdateBootVersion;
import org.springframework.ide.vscode.boot.validation.generations.VersionValidator;
import org.springframework.ide.vscode.commons.java.IJavaProject;
import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder;
import org.springframework.ide.vscode.commons.languageserver.java.ProjectObserver;
import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer;
@Configuration(proxyBeanMethods = false)
public class BootVersionValidationConfig {
private static final Logger log = LoggerFactory.getLogger(BootVersionValidationConfig.class);
@Bean UpdateBootVersion updateBootVersion(SimpleLanguageServer server, Optional<SpringBootUpgrade> bootUpgradeOpt) {
return new UpdateBootVersion(server.getDiagnosticSeverityProvider(), bootUpgradeOpt);
}
@Bean ProjectVersionDiagnosticProvider projectVersionDiagnosticProvider(List<VersionValidator> validators) {
return new ProjectVersionDiagnosticProvider(validators);
}
@ConditionalOnMissingClass("org.springframework.ide.vscode.languageserver.testharness.LanguageServerHarness")
@ConditionalOnProperty(prefix = "languageserver", name = "reconcile-only-opened-docs", havingValue = "false", matchIfMissing = true)
@Bean
ProjectReconcileScheduler bootVersionValidationScheduler(SimpleLanguageServer server,
JavaProjectFinder projectFinder, BootJavaConfig config, ProjectObserver projectObserver,
ProjectVersionDiagnosticProvider diagnosticProvider) {
return new ProjectReconcileScheduler(server,
new BootVersionValidationEngine(server, config, projectObserver, projectFinder, diagnosticProvider),
projectFinder) {
@Override
protected void init() {
super.init();
config.addListener(evt -> scheduleValidationForAllProjects());
projectObserver.addListener(new ProjectObserver.Listener() {
@Override
public void deleted(IJavaProject project) {
unscheduleValidation(project);
clear(project, true);
}
@Override
public void created(IJavaProject project) {
scheduleValidation(project);
}
@Override
public void changed(IJavaProject project) {
scheduleValidation(project);
}
});
log.info("Started Boot Version reconciler");
}
};
}
}

View File

@@ -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
@@ -309,7 +309,7 @@ public class RewriteRecipeRepository implements ApplicationContextAware {
for (RecipeCodeActionDescriptor d : descriptors) {
TreeVisitor<?, ExecutionContext> markVisitor = d.getMarkerVisitor(applicationContext);
if (markVisitor != null) {
cu = (CompilationUnit) markVisitor.visit(cu, new InMemoryExecutionContext(e -> log.error("", e)));
cu = (CompilationUnit) markVisitor.visit(cu, new InMemoryExecutionContext(e -> log.error("Marker visitor failed!", e)));
}
}
return cu;
@@ -424,7 +424,7 @@ public class RewriteRecipeRepository implements ApplicationContextAware {
});
List<SourceFile> sources = projectParser.parse(absoluteProjectDir, getClasspathEntries(project));
server.getProgressService().progressEvent(progressToken, "Computing changes...");
RecipeRun reciperun = r.run(sources, new InMemoryExecutionContext(e -> log.error("", e)));
RecipeRun reciperun = r.run(sources, new InMemoryExecutionContext(e -> log.error("Recipe execution failed", e)));
List<Result> results = reciperun.getResults();
return ORDocUtils.createWorkspaceEdit(absoluteProjectDir, server.getTextDocumentService(), results);
}

View File

@@ -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
@@ -14,6 +14,8 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import org.eclipse.lsp4j.TextDocumentIdentifier;
@@ -34,8 +36,8 @@ public class SpringBootUpgrade {
final public static String CMD_UPGRADE_SPRING_BOOT = "sts/upgrade/spring-boot";
private static final Map<String, String> VERSION_TO_RECIPE_ID = Map.of(
"2.0", "org.openrewrite.java.spring.boot2.SpringBoot1To2Migration",
private final Map<String, String> versionsToRecipeId = Map.of(
"2.0", "org.openrewrite.java.spring.boot2.UpgradeSpringBoot_2_0",
"2.1", "org.openrewrite.java.spring.boot2.UpgradeSpringBoot_2_1",
"2.2", "org.openrewrite.java.spring.boot2.UpgradeSpringBoot_2_2",
"2.3", "org.openrewrite.java.spring.boot2.UpgradeSpringBoot_2_3",
@@ -46,6 +48,11 @@ public class SpringBootUpgrade {
"3.0", "org.openrewrite.java.spring.boot3.UpgradeSpringBoot_3_0"
);
// private static final Map<Integer, String> MAJOR_VERSION_TO_RECIPE_ID = Map.of(
// 2, "org.openrewrite.java.spring.boot2.SpringBoot1To2Migration",
// 3, "org.openrewrite.java.spring.boot3.SpringBoot2To3Migration"
// );
private RewriteRecipeRepository recipeRepo;
public SpringBootUpgrade(SimpleLanguageServer server, RewriteRecipeRepository recipeRepo, JavaProjectFinder projectFinder) {
@@ -70,7 +77,7 @@ public class SpringBootUpgrade {
+ version.toMajorMinorVersionStr() + "' is newer or same as the target version '"
+ targetVersion.toMajorMinorVersionStr() + "'");
return recipeRepo.loaded.thenComposeAsync(loade -> recipeRepo.apply(
return recipeRepo.loaded.thenComposeAsync(load -> recipeRepo.apply(
createUpgradeRecipe(version, targetVersion),
uri,
UUID.randomUUID().toString()
@@ -78,10 +85,10 @@ public class SpringBootUpgrade {
});
}
static List<String> createRecipeIdsChain(int major, int minor, int targetMajor, int targetMinor) {
static List<String> createRecipeIdsChain(int major, int minor, int targetMajor, int targetMinor, Map<String, String> versionToRecipeId) {
List<String> ids = new ArrayList<>();
for (int currentMajor = major, currentMinor = minor; targetMajor > currentMajor || (targetMajor == currentMajor && currentMinor <= targetMinor);) {
String recipeId = VERSION_TO_RECIPE_ID.get(createVersionString(currentMajor, currentMinor));
String recipeId = versionToRecipeId.get(createVersionString(currentMajor, currentMinor));
if (recipeId == null) {
currentMajor++;
currentMinor = 0;
@@ -101,11 +108,15 @@ public class SpringBootUpgrade {
// patch version upgrade - treat as pom versions only upgrade
recipe.doNext(new UpgradeDependencyVersion("org.springframework.boot", "*", version.getMajor() + "." + version.getMinor() + ".x", null, null, null));
recipe.doNext(new UpgradeParentVersion("org.springframework.boot", "spring-boot-starter-parent", version.getMajor() + "." + version.getMinor() + ".x", null, null));
} else {
createRecipeIdsChain(version.getMajor(), version.getMinor(), targetVersion.getMajor(), targetVersion.getMinor()).stream()
.map(recipeRepo::getRecipe)
.filter(o -> o.isPresent())
.forEach(o -> recipe.doNext(o.get()));
} else /*if (version.getMajor() == targetVersion.getMajor())*/ {
List<String> recipedIds = createRecipeIdsChain(version.getMajor(), version.getMinor(), targetVersion.getMajor(), targetVersion.getMinor(), versionsToRecipeId);
if (!recipedIds.isEmpty()) {
String recipeId = recipedIds.get(recipedIds.size() - 1);
recipeRepo.getRecipe(recipeId).ifPresent(recipe::doNext);
}
// } else {
// String recipeId = MAJOR_VERSION_TO_RECIPE_ID.get(targetVersion.getMajor());
// recipeRepo.getRecipe(recipeId).ifPresent(recipe::doNext);
}
if (recipe.getRecipeList().isEmpty()) {
@@ -125,5 +136,18 @@ public class SpringBootUpgrade {
return sb.toString();
}
static String nearestAvailableMinorVersion(Version v, Set<String> availableVersions) {
for (int major = v.getMajor(), minor = v.getMinor(); minor >= 0; minor--) {
String versionStr = createVersionString(major, minor);
if (availableVersions.contains(versionStr)) {
return versionStr;
}
}
return null;
}
public Optional<String> getNearestAvailableMinorVersion(Version v) {
return Optional.ofNullable(nearestAvailableMinorVersion(v, versionsToRecipeId.keySet()));
}
}

View File

@@ -19,11 +19,6 @@ 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.preferences.VersionValidationPreferences;
import org.springframework.ide.vscode.commons.java.IJavaProject;
import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder;
import org.springframework.ide.vscode.commons.languageserver.java.ProjectObserver;
@@ -33,12 +28,15 @@ public class BootVersionValidationEngine implements IJavaProjectReconcileEngine
private static final Logger log = LoggerFactory.getLogger(BootVersionValidationEngine.class);
private SimpleLanguageServer server;
private BootJavaConfig config;
private final SimpleLanguageServer server;
private final BootJavaConfig config;
private final ProjectVersionDiagnosticProvider diagnosticProvider;
public BootVersionValidationEngine(SimpleLanguageServer server, BootJavaConfig config, ProjectObserver projectObserver, JavaProjectFinder projectFinder) {
public BootVersionValidationEngine(SimpleLanguageServer server, BootJavaConfig config, ProjectObserver projectObserver, JavaProjectFinder projectFinder,
ProjectVersionDiagnosticProvider diagnosticProvider) {
this.server = server;
this.config = config;
this.diagnosticProvider = diagnosticProvider;
}
public void reconcile(IJavaProject project) {
@@ -46,15 +44,6 @@ public class BootVersionValidationEngine implements IJavaProjectReconcileEngine
log.debug("validating Spring Boot version on project: " + project.getElementName());
long start = System.currentTimeMillis();
VersionValidationPreferences preferences = new VersionValidationPreferences();
String url = getSpringProjectsUrl(preferences);
SpringProjectsClient client = new SpringProjectsClient(url);
SpringProjectsProvider provider = new SpringIoProjectsProvider(client);
VersionValidators validators = new VersionValidators(server.getDiagnosticSeverityProvider(), provider);
ProjectVersionDiagnosticProvider diagnosticProvider = new ProjectVersionDiagnosticProvider(validators);
try {
DiagnosticResult result = diagnosticProvider.getDiagnostics(project);
if (result != null && !result.getDiagnostics().isEmpty()) {
@@ -72,10 +61,6 @@ public class BootVersionValidationEngine implements IJavaProjectReconcileEngine
}
}
private String getSpringProjectsUrl(VersionValidationPreferences preferences) {
return preferences.getSpringProjectsUrl();
}
@Override
public void clear(IJavaProject project) {
// Build file

View File

@@ -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
@@ -23,7 +23,8 @@ import org.springframework.ide.vscode.commons.languageserver.reconcile.Diagnosti
abstract public class AbstractDiagnosticValidator implements VersionValidator {
private static final String BOOT_VERSION_VALIDATION_CODE = "BOOT_VERSION_VALIDATION_CODE";
private final DiagnosticSeverityProvider diagnosticSeverityProvider;
public AbstractDiagnosticValidator(DiagnosticSeverityProvider diagnosticSeverityProvider) {
@@ -39,7 +40,7 @@ abstract public class AbstractDiagnosticValidator implements VersionValidator {
}
Diagnostic diagnostic = new Diagnostic();
diagnostic.setCode(VersionValidators.BOOT_VERSION_VALIDATION_CODE);
diagnostic.setCode(BOOT_VERSION_VALIDATION_CODE);
diagnostic.setMessage(diagnosticMessage.toString());
Range range = new Range();

View File

@@ -28,9 +28,9 @@ public class ProjectVersionDiagnosticProvider {
private static final Logger log = LoggerFactory.getLogger(ProjectVersionDiagnosticProvider.class);
private final VersionValidators validators;
private final List<VersionValidator> validators;
public ProjectVersionDiagnosticProvider(VersionValidators validators) {
public ProjectVersionDiagnosticProvider(List<VersionValidator> validators) {
this.validators = validators;
}
@@ -51,7 +51,7 @@ public class ProjectVersionDiagnosticProvider {
List<Diagnostic> diagnostics = new ArrayList<Diagnostic>();
for (VersionValidator validator : validators.getValidators()) {
for (VersionValidator validator : validators) {
try {
Collection<Diagnostic> batch = validator.validate(javaProject, javaProjectVersion);
if (batch != null) {

View File

@@ -0,0 +1,121 @@
/*******************************************************************************
* Copyright (c) 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.validation.generations;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
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.preferences.VersionValidationProblemType;
import org.springframework.ide.vscode.commons.java.IJavaProject;
import org.springframework.ide.vscode.commons.java.Version;
import org.springframework.ide.vscode.commons.languageserver.reconcile.DiagnosticSeverityProvider;
import com.google.common.collect.ImmutableList;
public class UpdateBootVersion extends AbstractDiagnosticValidator {
private Optional<SpringBootUpgrade> bootUpgradeOpt;
public UpdateBootVersion(DiagnosticSeverityProvider diagnosticSeverityProvider, Optional<SpringBootUpgrade> bootUpgradeOpt) {
super(diagnosticSeverityProvider);
this.bootUpgradeOpt = bootUpgradeOpt;
}
@Override
public Collection<Diagnostic> validate(IJavaProject javaProject, Version javaProjectVersion) throws Exception {
List<Version> versions = CachedBootVersionsFromMavenCentral.getBootVersions();
ImmutableList.Builder<Diagnostic> 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 Optional<Diagnostic> validateMajorVersion(IJavaProject javaProject, Version javaProjectVersion, List<Version> sortedBootVersions) {
Version latest = VersionValidationUtils.getNewerLatestMajorRelease(sortedBootVersions, javaProjectVersion);
if (latest != null) {
VersionValidationProblemType problemType = VersionValidationProblemType.UPDATE_LATEST_MAJOR_VERSION;
StringBuffer message = new StringBuffer();
message.append("Newer major version of Spring Boot available: ");
message.append(latest.toString());
CodeAction ca = bootUpgradeOpt.flatMap(bu -> bu.getNearestAvailableMinorVersion(latest)).map(targetVersion -> {
CodeAction c = new CodeAction();
c.setKind(CodeActionKind.QuickFix);
c.setTitle("Upgrade to Spring Boot " + targetVersion + " (executes the full project conversion recipe from OpenRewrite)");
String commandId = SpringBootUpgrade.CMD_UPGRADE_SPRING_BOOT;
c.setCommand(new Command("Upgrade to Version " + targetVersion, commandId,
ImmutableList.of(javaProject.getLocationUri().toASCIIString(), targetVersion)));
return c;
}).orElse(null);
return Optional.ofNullable(createDiagnostic(ca, problemType, message.toString()));
}
return Optional.empty();
}
private Optional<Diagnostic> validateMinorVersion(IJavaProject javaProject, Version javaProjectVersion, List<Version> 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 = bootUpgradeOpt.flatMap(bu -> bu.getNearestAvailableMinorVersion(latest)).map(targetVersion -> {
CodeAction c = new CodeAction();
c.setKind(CodeActionKind.QuickFix);
c.setTitle("Upgrade to Spring Boot " + targetVersion + " (executes the full project conversion recipe from OpenRewrite)");
String commandId = SpringBootUpgrade.CMD_UPGRADE_SPRING_BOOT;
c.setCommand(new Command("Upgrade to Version " + targetVersion, commandId,
ImmutableList.of(javaProject.getLocationUri().toASCIIString(), targetVersion)));
return c;
}).orElse(null);
return Optional.ofNullable(createDiagnostic(ca, problemType, message.toString()));
}
return Optional.empty();
}
private Optional<Diagnostic> validatePatchVersion(IJavaProject javaProject, Version javaProjectVersion, List<Version> 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 = bootUpgradeOpt.map(bu -> {
CodeAction c = new CodeAction();
c.setKind(CodeActionKind.QuickFix);
c.setTitle("Upgrade to Spring Boot " + latest.toString() + " (Maven dependency version changes only)");
String commandId = SpringBootUpgrade.CMD_UPGRADE_SPRING_BOOT;
c.setCommand(new Command("Upgrade to Version " + latest.toString(), commandId,
ImmutableList.of(javaProject.getLocationUri().toASCIIString(), latest.toString())));
return c;
}).orElse(null);
return Optional.ofNullable(createDiagnostic(ca, problemType, message.toString()));
}
return Optional.empty();
}
}

View File

@@ -11,26 +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.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.preferences.VersionValidationProblemType;
import org.springframework.ide.vscode.commons.java.IJavaProject;
import org.springframework.ide.vscode.commons.java.Version;
import org.springframework.ide.vscode.commons.languageserver.reconcile.DiagnosticSeverityProvider;
import com.google.common.collect.ImmutableList;
/*
* TODO: Once uncommented to bring in Spring.io generations API support:
* 1. Remaining validators must become beans.
* 2. I'd merge all 4 into one single validator
*/
public class VersionValidators {
public static final String BOOT_VERSION_VALIDATION_CODE = "BOOT_VERSION_VALIDATION_CODE";
private final VersionValidator[] validators;
public VersionValidators(DiagnosticSeverityProvider diagnosticSeverityProvider, SpringProjectsProvider provider) {
@@ -39,7 +30,6 @@ public class VersionValidators {
// new UnsupportedCommercialValidator(diagnosticSeverityProvider, provider),
// new UnsupportedOssValidator(diagnosticSeverityProvider, provider),
// new SupportedCommercialValidator(diagnosticSeverityProvider, provider),
new UpdateBootVersion(diagnosticSeverityProvider)
};
}
@@ -177,91 +167,5 @@ public class VersionValidators {
// }
// }
private static class UpdateBootVersion extends AbstractDiagnosticValidator {
public UpdateBootVersion(DiagnosticSeverityProvider diagnosticSeverityProvider) {
super(diagnosticSeverityProvider);
}
@Override
public Collection<Diagnostic> validate(IJavaProject javaProject, Version javaProjectVersion) throws Exception {
List<Version> versions = CachedBootVersionsFromMavenCentral.getBootVersions();
ImmutableList.Builder<Diagnostic> 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 Optional<Diagnostic> validateMajorVersion(IJavaProject javaProject, Version javaProjectVersion, List<Version> sortedBootVersions) {
Version latest = VersionValidationUtils.getNewerLatestMajorRelease(sortedBootVersions, javaProjectVersion);
if (latest != null) {
VersionValidationProblemType problemType = VersionValidationProblemType.UPDATE_LATEST_MAJOR_VERSION;
StringBuffer message = new StringBuffer();
message.append("Newer major 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<Diagnostic> validateMinorVersion(IJavaProject javaProject, Version javaProjectVersion, List<Version> 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<Diagnostic> validatePatchVersion(IJavaProject javaProject, Version javaProjectVersion, List<Version> 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();
}
}
}

View File

@@ -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,27 +11,43 @@
package org.springframework.ide.vscode.boot.java.rewrite;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.junit.jupiter.api.Test;
import org.springframework.ide.vscode.commons.java.Version;
public class SpringBootUpgradeTest {
private static final Map<String, String> MINOR_VERSION_TO_RECIPE_ID = Map.of(
"2.0", "org.openrewrite.java.spring.boot2.UpgradeSpringBoot_2_0",
"2.1", "org.openrewrite.java.spring.boot2.UpgradeSpringBoot_2_1",
"2.2", "org.openrewrite.java.spring.boot2.UpgradeSpringBoot_2_2",
"2.3", "org.openrewrite.java.spring.boot2.UpgradeSpringBoot_2_3",
"2.4", "org.openrewrite.java.spring.boot2.UpgradeSpringBoot_2_4",
"2.5", "org.openrewrite.java.spring.boot2.UpgradeSpringBoot_2_5",
"2.6", "org.openrewrite.java.spring.boot2.UpgradeSpringBoot_2_6",
"2.7", "org.openrewrite.java.spring.boot2.UpgradeSpringBoot_2_7",
"3.0", "org.openrewrite.java.spring.boot3.UpgradeSpringBoot_3_0"
);
@Test
void recipeIdChain1() throws Exception {
void recipeIdChain_1() throws Exception {
assertEquals(List.of(
"org.openrewrite.java.spring.boot2.SpringBoot1To2Migration",
"org.openrewrite.java.spring.boot2.UpgradeSpringBoot_2_0",
"org.openrewrite.java.spring.boot2.UpgradeSpringBoot_2_1",
"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"
), SpringBootUpgrade.createRecipeIdsChain(1, 3, 2, 5));
), SpringBootUpgrade.createRecipeIdsChain(1, 3, 2, 5, MINOR_VERSION_TO_RECIPE_ID));
}
@Test
void recipeIdChain2() throws Exception {
void recipeIdChain_2() throws Exception {
assertEquals(List.of(
"org.openrewrite.java.spring.boot2.UpgradeSpringBoot_2_2",
"org.openrewrite.java.spring.boot2.UpgradeSpringBoot_2_3",
@@ -39,13 +55,13 @@ public class SpringBootUpgradeTest {
"org.openrewrite.java.spring.boot2.UpgradeSpringBoot_2_5",
"org.openrewrite.java.spring.boot2.UpgradeSpringBoot_2_6",
"org.openrewrite.java.spring.boot2.UpgradeSpringBoot_2_7"
), SpringBootUpgrade.createRecipeIdsChain(2, 2, 2, 7));
), SpringBootUpgrade.createRecipeIdsChain(2, 2, 2, 7, MINOR_VERSION_TO_RECIPE_ID));
}
@Test
void recipeIdChain3() throws Exception {
void recipeIdChain_3() throws Exception {
assertEquals(List.of(
"org.openrewrite.java.spring.boot2.SpringBoot1To2Migration",
"org.openrewrite.java.spring.boot2.UpgradeSpringBoot_2_0",
"org.openrewrite.java.spring.boot2.UpgradeSpringBoot_2_1",
"org.openrewrite.java.spring.boot2.UpgradeSpringBoot_2_2",
"org.openrewrite.java.spring.boot2.UpgradeSpringBoot_2_3",
@@ -54,20 +70,29 @@ public class SpringBootUpgradeTest {
"org.openrewrite.java.spring.boot2.UpgradeSpringBoot_2_6",
"org.openrewrite.java.spring.boot2.UpgradeSpringBoot_2_7",
"org.openrewrite.java.spring.boot3.UpgradeSpringBoot_3_0"
), SpringBootUpgrade.createRecipeIdsChain(1, 3, 3, 0));
), SpringBootUpgrade.createRecipeIdsChain(1, 3, 3, 0, MINOR_VERSION_TO_RECIPE_ID));
}
@Test
void recipeIdChain4() throws Exception {
void recipeIdChain_4() throws Exception {
assertEquals(List.of(
"org.openrewrite.java.spring.boot2.UpgradeSpringBoot_2_2"
), SpringBootUpgrade.createRecipeIdsChain(2, 2, 2, 2));
), SpringBootUpgrade.createRecipeIdsChain(2, 2, 2, 2, MINOR_VERSION_TO_RECIPE_ID));
}
@Test
void recipeIdChain5() throws Exception {
void recipeIdChain_5() throws Exception {
assertEquals(List.of(
), SpringBootUpgrade.createRecipeIdsChain(2, 7, 2, 2));
), SpringBootUpgrade.createRecipeIdsChain(2, 7, 2, 2, MINOR_VERSION_TO_RECIPE_ID));
}
@Test
void nearestMinorVersion() throws Exception {
assertEquals("3.0", SpringBootUpgrade.nearestAvailableMinorVersion(new Version(3, 0, 2, null), MINOR_VERSION_TO_RECIPE_ID.keySet()));
assertEquals("3.0", SpringBootUpgrade.nearestAvailableMinorVersion(new Version(3, 3, 1, null), MINOR_VERSION_TO_RECIPE_ID.keySet()));
assertNull(SpringBootUpgrade.nearestAvailableMinorVersion(new Version(4, 3, 1, null), MINOR_VERSION_TO_RECIPE_ID.keySet()));
assertNull(SpringBootUpgrade.nearestAvailableMinorVersion(new Version(1, 5, 0, null), MINOR_VERSION_TO_RECIPE_ID.keySet()));
assertNull(SpringBootUpgrade.nearestAvailableMinorVersion(new Version(3, 0, 2, null), Collections.emptySet()));
}
}