Update Boot Version validation

Re-implemented boot version validation support using spring.io
Generations
This commit is contained in:
Nieraj Singh
2022-11-07 07:42:31 -08:00
parent de5cc39103
commit ddeff8882e
12 changed files with 444 additions and 365 deletions

View File

@@ -23,17 +23,14 @@ import org.slf4j.LoggerFactory;
public class SpringProjectUtil {
public static final String SPRING_BOOT = "spring-boot";
// Pattern copied from https://semver.org/
private static final String VERSION_PATTERN_STR = "(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:(-|\\.)((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?";
private static final String GENERATION_VERSION_STR = "([0-9]+)";
public static final Logger log = LoggerFactory.getLogger(SpringProjectUtil.class);
private static final Pattern MAJOR_MINOR_VERSION = Pattern.compile("(0|[1-9]\\d*)\\.(0|[1-9]\\d*)");
// Pattern copied from https://semver.org/
private static final Pattern VERSION = Pattern.compile(VERSION_PATTERN_STR);
private static final Pattern SPRING_NAME = Pattern.compile("([a-z]+)(-[a-z]+)*");
private static final Pattern GENERATION_VERSION = Pattern.compile(GENERATION_VERSION_STR);
public static boolean isSpringProject(IJavaProject jp) {
return hasSpecificLibraryOnClasspath(jp, "spring-core", true);
@@ -46,40 +43,40 @@ public class SpringProjectUtil {
public static boolean hasBootActuators(IJavaProject jp) {
return hasSpecificLibraryOnClasspath(jp, "spring-boot-actuator-", true);
}
public static String getMajMinVersion(String name) {
Matcher matcher = MAJOR_MINOR_VERSION.matcher(name);
if (matcher.find()) {
int start = matcher.start();
int end = matcher.end();
return name.substring(start, end);
}
return null;
}
public static String getVersion(String name) {
Matcher matcher = VERSION.matcher(name);
if (matcher.find()) {
int start = matcher.start();
int end = matcher.end();
return name.substring(start, end);
}
return null;
}
/**
*
* @param libName e.g. spring-boot-3.0.0.RELEASE.jar
* @return "slug" portion: "spring-boot"
* Parses version from the given generation name (e.g. "2.1.x"
* @param name
* @return Version if valid generation name with major and minor components
* @throws Exception if invalid generation name
*/
public static String getProjectSlug(String libName) {
Matcher matcher = SPRING_NAME.matcher(libName);
public static Version getVersionFromGeneration(String name) throws Exception {
Matcher matcher = GENERATION_VERSION.matcher(name);
String major = null;
String minor = null;
if (matcher.find()) {
int start = matcher.start();
int end = matcher.end();
return libName.substring(start, end);
major = name.substring(start, end);
}
return null;
if (matcher.find()) {
int start = matcher.start();
int end = matcher.end();
minor = name.substring(start, end);
}
if (major != null && minor != null) {
return new Version(
Integer.parseInt(major),
Integer.parseInt(minor),
0,
null
);
}
throw new IllegalArgumentException("Invalid semver. Unable to parse major and minor version from: " + name);
}
public static List<File> getLibrariesOnClasspath(IJavaProject jp, String libraryNamePrefix) {

View File

@@ -63,9 +63,6 @@ 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.generations.SampleProjectsProvider;
import org.springframework.ide.vscode.boot.validation.generations.SpringIoProjectsProvider;
import org.springframework.ide.vscode.boot.validation.generations.SpringProjectsValidations;
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;
@@ -93,7 +90,6 @@ import org.springframework.ide.vscode.languageserver.starter.LanguageServerRunne
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.constructor.SafeConstructor;
import com.google.common.collect.ImmutableList;
import com.google.gson.Gson;
import com.google.gson.JsonElement;
@@ -291,13 +287,6 @@ public class BootLanguageServerBootApp {
};
}
@Bean SpringProjectsValidations springProjectsValidations(SimpleLanguageServer server) {
return new SpringProjectsValidations(server, ImmutableList.of(
new SpringIoProjectsProvider(),
new SampleProjectsProvider()
));
}
@Bean FutureProjectFinder futureProjectFinder(JavaProjectFinder projectFinder, Optional<ProjectObserver> projectObserver) {
return new FutureProjectFinder(projectFinder, projectObserver);
}

View File

@@ -18,7 +18,6 @@ import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.eclipse.lsp4j.MessageType;
import org.eclipse.lsp4j.TextDocumentIdentifier;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -37,11 +36,8 @@ 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.generations.ProjectValidation;
import org.springframework.ide.vscode.boot.validation.generations.SpringProjectsValidations;
import org.springframework.ide.vscode.boot.xml.SpringXMLLanguageServerComponents;
import org.springframework.ide.vscode.commons.java.IJavaProject;
import org.springframework.ide.vscode.commons.languageserver.DiagnosticService;
import org.springframework.ide.vscode.commons.languageserver.completion.CompositeCompletionEngine;
import org.springframework.ide.vscode.commons.languageserver.completion.ICompletionEngine;
import org.springframework.ide.vscode.commons.languageserver.completion.VscodeCompletionEngineAdapter;
@@ -51,7 +47,6 @@ import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFin
import org.springframework.ide.vscode.commons.languageserver.java.ProjectObserver;
import org.springframework.ide.vscode.commons.languageserver.reconcile.IReconcileEngine;
import org.springframework.ide.vscode.commons.languageserver.util.HoverHandler;
import org.springframework.ide.vscode.commons.languageserver.util.ShowMessageException;
import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer;
import org.springframework.ide.vscode.commons.languageserver.util.SimpleTextDocumentService;
import org.springframework.ide.vscode.commons.util.text.TextDocument;
@@ -85,7 +80,6 @@ public class BootLanguageServerInitializer implements InitializingBean {
@Autowired BootJavaConfig config;
@Autowired SpringSymbolIndex springIndexer;
@Autowired(required = false) List<ICompletionEngine> completionEngines;
@Autowired SpringProjectsValidations springProjectsValidations;
@Autowired private JavaProjectFinder projectFinder;
@Autowired private LanguageServerProperties configProps;
@Autowired(required = false) private RewriteRecipeRepository recipesRepo;
@@ -180,9 +174,7 @@ public class BootLanguageServerInitializer implements InitializingBean {
if (recipesRepo != null) {
recipesRepo.onRecipesLoaded(v -> reconcile());
}
addSpringProjectsVersionValidation(params);
server.getWorkspaceService().getFileObserver().onFilesChanged(FILES_TO_WATCH_GLOB, this::handleFiles);
server.getWorkspaceService().getFileObserver().onFilesCreated(FILES_TO_WATCH_GLOB, this::handleFiles);
@@ -210,51 +202,6 @@ public class BootLanguageServerInitializer implements InitializingBean {
}
}
private void addSpringProjectsVersionValidation(BootLanguageServerParams params2) {
params.projectObserver.addListener(
new ProjectObserver.Listener() {
@Override
public void deleted(IJavaProject project) {
// TODO Auto-generated method stub
}
@Override
public void created(IJavaProject project) {
// TODO: PT 173730396 - As of 4.9.1, Spring Project Version validation is not yet
// available from https://spring.io/api/projects
// Commented out to disable this feature. Uncomment to test if necessary
// validateProjectVersion(project);
}
@Override
public void changed(IJavaProject project) {
// TODO Auto-generated method stub
}
private void validateProjectVersion(IJavaProject project) {
try {
ProjectValidation validation = springProjectsValidations.validateVersion(project);
if (validation != null && validation.getMessageType() == MessageType.Warning) {
// TODO: replace this with a diagnostic message that has project-scope.
// At the moment of implementing this, it doesnt look like sending a diagnostic
// message to LSP4E without a resource context is possible.
DiagnosticService diagnosticService = server.getDiagnosticService();
if (diagnosticService != null) {
diagnosticService.diagnosticEvent(
ShowMessageException.warning(validation.getMessage(), null));
}
log.warn(validation.getMessage());
}
} catch (Exception e) {
log.error("Failed validating Spring Project version", e);
}
}
});
}
private void validateProject(IJavaProject project, IReconcileEngine reconcileEngine) {
if (configProps.isReconcileOnlyOpenedDocs()) {
return;

View File

@@ -0,0 +1,70 @@
/*******************************************************************************
* Copyright (c) 2022 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 org.eclipse.lsp4j.TextDocumentIdentifier;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ide.vscode.boot.validation.generations.SpringBootProjectValidations;
import org.springframework.ide.vscode.boot.validation.generations.SpringIoProjectsProvider;
import org.springframework.ide.vscode.boot.validation.generations.SpringProjectDiagnostic;
import org.springframework.ide.vscode.boot.validation.generations.SpringProjectsClient;
import org.springframework.ide.vscode.boot.validation.generations.SpringProjectsProvider;
import org.springframework.ide.vscode.commons.java.IJavaProject;
import org.springframework.ide.vscode.commons.languageserver.java.ProjectObserver;
import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer;
import org.springframework.stereotype.Component;
@Component
public class BootVersionValidator {
private static final Logger log = LoggerFactory.getLogger(BootVersionValidator.class);
public BootVersionValidator(SimpleLanguageServer server, ProjectObserver observer) {
observer.addListener(new ProjectObserver.Listener() {
@Override
public void deleted(IJavaProject project) {
}
@Override
public void created(IJavaProject project) {
String url = "https://spring.io/api/projects";
SpringProjectsClient client = new SpringProjectsClient(url);
SpringProjectsProvider provider = new SpringIoProjectsProvider(client);
SpringBootProjectValidations validations = new SpringBootProjectValidations(provider);
try {
List<SpringProjectDiagnostic> diagnostics = validations.validateBootVersion(project);
if (diagnostics != null) {
for (SpringProjectDiagnostic springProjectDiagnostic : diagnostics) {
server.getTextDocumentService().publishDiagnostics(new TextDocumentIdentifier(springProjectDiagnostic.getUri().toString()), List.of(springProjectDiagnostic.getDiagnostic()));
}
}
} catch (Exception e) {
log.error("Failed validating Spring Project version", e);
}
}
@Override
public void changed(IJavaProject project) {
}
});
}
}

View File

@@ -0,0 +1,56 @@
/*******************************************************************************
* Copyright (c) 2022 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.io.File;
import java.net.URI;
import java.util.List;
import org.springframework.ide.vscode.boot.validation.generations.json.Generation;
import org.springframework.ide.vscode.boot.validation.generations.json.Generations;
import org.springframework.ide.vscode.commons.java.IJavaProject;
import org.springframework.ide.vscode.commons.java.SpringProjectUtil;
import org.springframework.ide.vscode.commons.java.Version;
public abstract class BootDiagnosticProvider {
public static final String BOOT_VERSION_VALIDATION_CODE = "BOOT_VERSION_VALIDATION_CODE";;
/**
*
* @param javaProject
* @param info Spring Dependency Info
* @param Spring project Generation information
* @return Diagnostic if applicable to the given version, or null
*/
abstract SpringProjectDiagnostic getDiagnostic(IJavaProject javaProject, SpringDependencyInfo info, Generations generations) throws Exception;
protected URI getBuildFileUri(IJavaProject javaProject) throws Exception {
// TODO: add gradle support here
return getPomUri(javaProject);
}
private URI getPomUri(IJavaProject project) throws Exception {
return new URI(project.getLocationUri().toString() + "/pom.xml");
}
protected File getSpringBootDependency(IJavaProject project) {
List<File> libs = SpringProjectUtil.getLibrariesOnClasspath(project, "spring-boot");
return libs != null && libs.size() > 0 ? libs.get(0) : null;
}
protected Version getVersion(Generation generation) throws Exception {
return SpringProjectUtil.getVersionFromGeneration(generation.getName());
}
}

View File

@@ -1,34 +0,0 @@
/*******************************************************************************
* Copyright (c) 2021 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
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.boot.validation.generations;
import org.eclipse.lsp4j.MessageType;
public class ProjectValidation {
public static ProjectValidation OK = new ProjectValidation("", MessageType.Info);
private final MessageType messageType;
private final String message;
public ProjectValidation(String message, MessageType messageType) {
this.messageType = messageType;
this.message = message;
}
public String getMessage() {
return this.message;
}
public MessageType getMessageType() {
return messageType;
}
}

View File

@@ -0,0 +1,50 @@
/*******************************************************************************
* Copyright (c) 2020, 2022 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.List;
import org.springframework.ide.vscode.boot.validation.generations.json.Generations;
import org.springframework.ide.vscode.commons.java.IJavaProject;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableList.Builder;
public class SpringBootProjectValidations {
private final SpringProjectsProvider projectsProvider;
private static final String SPRING_BOOT_PROJECT_SLUG = "spring-boot";
private final BootDiagnosticProvider[] diagnosticProviders = new BootDiagnosticProvider[] {
new UnsupportedVersionDiagnostic()
};
public SpringBootProjectValidations(SpringProjectsProvider projectsProvider) {
this.projectsProvider = projectsProvider;
}
public List<SpringProjectDiagnostic> validateBootVersion(IJavaProject project) throws Exception {
Builder<SpringProjectDiagnostic> builder = ImmutableList.builder();
if (project != null) {
SpringDependencyInfo info = new SpringDependencyInfo(project, SPRING_BOOT_PROJECT_SLUG);
Generations generations = projectsProvider.getGenerations(SPRING_BOOT_PROJECT_SLUG);
for (BootDiagnosticProvider provider : diagnosticProviders) {
SpringProjectDiagnostic diagnostic = provider.getDiagnostic(project, info, generations);
if (diagnostic != null) {
builder.add(diagnostic);
}
}
}
return builder.build();
}
}

View File

@@ -10,10 +10,9 @@
*******************************************************************************/
package org.springframework.ide.vscode.boot.validation.generations;
import java.io.File;
import org.apache.commons.io.FilenameUtils;
import org.springframework.ide.vscode.commons.java.IJavaProject;
import org.springframework.ide.vscode.commons.java.SpringProjectUtil;
import org.springframework.ide.vscode.commons.java.Version;
/**
* Version info for a spring dependency.
@@ -22,33 +21,25 @@ import org.springframework.ide.vscode.commons.java.SpringProjectUtil;
* "spring-boot", the fullVersion "2.4.0-M4", and the majMin "2.4"
*
*/
public class SpringVersionInfo {
public class SpringDependencyInfo {
private final String slug;
private final String majMin;
private final String fullVersion;
private final Version version;
/**
*
* @param file spring for dependency, e.g. spring-boot-2.4.0-M4.jar
*/
public SpringVersionInfo(File file) {
String fileName = FilenameUtils.getBaseName(file.getName());
this.slug = SpringProjectUtil.getProjectSlug(fileName);
this.majMin = SpringProjectUtil.getMajMinVersion(fileName);
this.fullVersion = SpringProjectUtil.getVersion(fileName);
public SpringDependencyInfo(IJavaProject project, String slug) {
this.slug = slug;
this.version = SpringProjectUtil.getDependencyVersion(project, slug);
}
public String getSlug() {
return slug;
}
public String getMajMin() {
return majMin;
public Version getVersion() {
return version;
}
public String getFullVersion() {
return fullVersion;
}
}

View File

@@ -0,0 +1,34 @@
/*******************************************************************************
* Copyright (c) 2022 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.net.URI;
import org.eclipse.lsp4j.Diagnostic;
public class SpringProjectDiagnostic {
private final Diagnostic diagnostic;
private final URI uri;
public SpringProjectDiagnostic(Diagnostic diagnostic, URI uri) {
this.diagnostic = diagnostic;
this.uri = uri;
}
public Diagnostic getDiagnostic() {
return diagnostic;
}
public URI getUri() {
return uri;
}
}

View File

@@ -1,86 +0,0 @@
/*******************************************************************************
* Copyright (c) 2020, 2021 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
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.boot.validation.generations;
import java.io.File;
import java.sql.Date;
import java.util.List;
import org.eclipse.lsp4j.MessageType;
import org.springframework.ide.vscode.boot.validation.generations.json.Generation;
import org.springframework.ide.vscode.boot.validation.generations.json.Generations;
import org.springframework.ide.vscode.commons.java.IJavaProject;
import org.springframework.ide.vscode.commons.java.SpringProjectUtil;
import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer;
public class SpringProjectsValidations {
private final List<SpringProjectsProvider> projectsProviders;
private final SimpleLanguageServer server;
public SpringProjectsValidations(SimpleLanguageServer server, List<SpringProjectsProvider> projectsProviders) {
this.projectsProviders = projectsProviders;
this.server = server;
}
public ProjectValidation validateVersion(IJavaProject jp) throws Exception {
StringBuilder builder = new StringBuilder();
if (jp != null) {
List<File> librariesOnClasspath = SpringProjectUtil.getLibrariesOnClasspath(jp, "spring");
if (librariesOnClasspath != null) {
for (File file : librariesOnClasspath) {
SpringVersionInfo versionInfo = new SpringVersionInfo(file);
for (SpringProjectsProvider projectsProvider : projectsProviders) {
Generations generations = projectsProvider.getGenerations(versionInfo.getSlug());
if (generations != null) {
List<Generation> gens = generations.getGenerations();
if (gens != null) {
for (Generation gen : gens) {
resolveWarnings(gen, builder, versionInfo);
}
}
}
}
}
}
}
return builder.length() > 0 ?
new ProjectValidation(builder.toString(), MessageType.Warning)
: ProjectValidation.OK;
}
private void resolveWarnings(Generation gen, StringBuilder messages, SpringVersionInfo versionInfo) {
if (isInGeneration(versionInfo.getMajMin(), gen)) {
Date currentDate = new Date(System.currentTimeMillis());
Date ossEndDate = Date.valueOf(gen.getOssSupportEndDate());
Date commercialEndDate = Date.valueOf(gen.getCommercialSupportEndDate());
messages.append("Using ");
messages.append(versionInfo.getSlug());
messages.append(" version: ");
messages.append(versionInfo.getFullVersion());
if (currentDate.after(ossEndDate)) {
messages.append(" - OSS has ended on: ");
messages.append(gen.getOssSupportEndDate());
}
if (currentDate.after(commercialEndDate)) {
messages.append(" - Commercial support has ended on: ");
messages.append(gen.getCommercialSupportEndDate());
}
}
}
private boolean isInGeneration(String version, Generation g) {
return g.getName().contains(version);
}
}

View File

@@ -0,0 +1,193 @@
/*******************************************************************************
* Copyright (c) 2022 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.net.URI;
import java.sql.Date;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.lsp4j.CodeAction;
import org.eclipse.lsp4j.CodeActionKind;
import org.eclipse.lsp4j.Command;
import org.eclipse.lsp4j.Diagnostic;
import org.eclipse.lsp4j.DiagnosticSeverity;
import org.eclipse.lsp4j.Position;
import org.eclipse.lsp4j.Range;
import org.springframework.ide.vscode.boot.validation.generations.json.Generation;
import org.springframework.ide.vscode.boot.validation.generations.json.Generations;
import org.springframework.ide.vscode.commons.java.IJavaProject;
import org.springframework.ide.vscode.commons.java.Version;
import com.google.common.collect.ImmutableList;
public class UnsupportedVersionDiagnostic extends BootDiagnosticProvider {
@Override
public SpringProjectDiagnostic getDiagnostic(IJavaProject project, SpringDependencyInfo dependency, Generations generations) throws Exception {
URI uri = getBuildFileUri(project);
if (uri == null) {
return null;
}
List<Generation> genList = generations.getGenerations();
// The generation of the current dependency
Generation dependencyGeneration = null;
int dependencyMajor = dependency.getVersion().getMajor();
int dependencyMinor = dependency.getVersion().getMinor();
// Collect the latest versions of each major version that are still supported
List<Generation> latestSupportedPerMajor = new ArrayList<>();
if (genList != null && genList.size() > 1) {
// The very latest version is first in the list as the generations are ordered by version
latestSupportedPerMajor.add(genList.get(0));
for (int i = 1; i < genList.size(); i++) {
Generation toAdd = genList.get(i);
Version toAddVersion = getVersion(toAdd);
Generation lastAdded = latestSupportedPerMajor.get(latestSupportedPerMajor.size() - 1);
Version lastAddedVersion = getVersion(lastAdded);
if (isCommercialValid(toAdd) && isOssValid(toAdd) && toAddVersion.getMajor() < lastAddedVersion.getMajor()) {
latestSupportedPerMajor.add(toAdd);
}
if (toAddVersion.getMajor() == dependencyMajor
&& toAddVersion.getMinor() == dependencyMinor) {
dependencyGeneration = toAdd;
}
}
}
if (dependencyGeneration == null) {
throw new Exception("Unable to find Spring Generation for: " + dependency.getVersion().toString());
}
StringBuffer message = new StringBuffer();
DiagnosticSeverity severity = DiagnosticSeverity.Information;
if (isCommercialValid(dependencyGeneration) && isOssValid(dependencyGeneration)) {
message.append("OSS support ends on: ");
message.append(dependencyGeneration.getOssSupportEndDate());
message.append('\n');
message.append("Commercial supports ends on: ");
message.append(dependencyGeneration.getCommercialSupportEndDate());
} else {
Generation toUpgrade = null;
// Calculate latest supported version for the same major version as the dependency
// For example, if the dependency version is 1.5.0 and the latest supported is 1.8.0,
// find the generation for 1.8.0
for (Generation generation : latestSupportedPerMajor) {
Version dependencyVersion = getVersion(dependencyGeneration);
Version latest = getVersion(generation);
if (latest.getMajor() == dependencyVersion.getMajor()) {
toUpgrade = generation;
break;
}
}
if (toUpgrade == null) {
// if there are no supported versions in the dependency major range, upgrade to the very
// latest version
toUpgrade = latestSupportedPerMajor.get(0);
}
if (isCommercialValid(dependencyGeneration)) {
severity = DiagnosticSeverity.Warning;
message.append("Unsupported OSS. Support ended on: ");
message.append(dependencyGeneration.getOssSupportEndDate());
message.append('\n');
message.append("Commercial supports ends on: ");
message.append(dependencyGeneration.getCommercialSupportEndDate());
} else if (isOssValid(dependencyGeneration)) {
severity = DiagnosticSeverity.Warning;
message.append("OSS support ends on: ");
message.append(dependencyGeneration.getOssSupportEndDate());
message.append('\n');
message.append("Unsupported Commercial. Support ended on: ");
message.append(dependencyGeneration.getCommercialSupportEndDate());
} else {
// OSS and Commercial support have ended
severity = DiagnosticSeverity.Error;
message.append("Unsupported OSS. Support ended on: ");
message.append(dependencyGeneration.getOssSupportEndDate());
message.append('\n');
message.append("Unsupported Commercial. Support ended on: ");
message.append(dependencyGeneration.getCommercialSupportEndDate());
}
message.append('\n');
message.append("Please upgrade to a newer supported version: ");
message.append(getVersion(toUpgrade).toString());
}
Diagnostic diagnostic = new Diagnostic();
diagnostic.setCode(BOOT_VERSION_VALIDATION_CODE);
diagnostic.setMessage(message.toString());
Range range = new Range();
Position start = new Position();
start.setLine(0);
start.setCharacter(0);
range.setStart(start);
Position end = new Position();
end.setLine(0);
end.setCharacter(1);
range.setEnd(end);
diagnostic.setRange(range);
diagnostic.setSeverity(severity);
setQuickfix(diagnostic);
return new SpringProjectDiagnostic(diagnostic, uri);
}
private void setQuickfix(Diagnostic diagnostic) {
// TODO: Fix this when open rewrite recipe quickfix becomes available.
Diagnostic refDiagnostic = new Diagnostic(diagnostic.getRange(), diagnostic.getMessage(), diagnostic.getSeverity(), diagnostic.getSource());
CodeAction ca = new CodeAction();
ca.setKind(CodeActionKind.QuickFix);
ca.setTitle("Validation FIX");
ca.setDiagnostics(List.of(refDiagnostic));
String commandId = "";
ca.setCommand(new Command("Validation FIX", commandId, ImmutableList.of()));
diagnostic.setData(ca);
}
private boolean isOssValid(Generation gen) {
Date currentDate = new Date(System.currentTimeMillis());
Date ossEndDate = Date.valueOf(gen.getOssSupportEndDate());
return currentDate.before(ossEndDate);
}
private boolean isCommercialValid(Generation gen) {
Date currentDate = new Date(System.currentTimeMillis());
Date commercialEndDate = Date.valueOf(gen.getCommercialSupportEndDate());
return currentDate.before(commercialEndDate);
}
}

View File

@@ -15,10 +15,8 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.io.File;
import java.util.List;
import org.eclipse.lsp4j.MessageType;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -26,25 +24,20 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Import;
import org.springframework.ide.vscode.boot.bootiful.BootLanguageServerTest;
import org.springframework.ide.vscode.boot.bootiful.HoverTestConf;
import org.springframework.ide.vscode.boot.validation.generations.ProjectValidation;
import org.springframework.ide.vscode.boot.validation.generations.SampleProjectsProvider;
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.SpringProjectsValidations;
import org.springframework.ide.vscode.boot.validation.generations.json.Generation;
import org.springframework.ide.vscode.boot.validation.generations.json.Generations;
import org.springframework.ide.vscode.boot.validation.generations.json.Link;
import org.springframework.ide.vscode.boot.validation.generations.json.SpringProject;
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.project.harness.BootLanguageServerHarness;
import org.springframework.ide.vscode.project.harness.ProjectsHarness;
import org.springframework.test.context.junit4.SpringRunner;
import com.google.common.collect.ImmutableList;
@RunWith(SpringRunner.class)
@BootLanguageServerTest
@Import(HoverTestConf.class)
@@ -61,89 +54,6 @@ public class ProjectGenerationsValidationTest {
harness.intialize(null);
}
@Test
public void testMajMinVersionParsing() throws Exception {
String version = SpringProjectUtil.getMajMinVersion("spring-boot-starter-batch-2.3.4.RELEASE");
assertEquals("2.3", version);
version = SpringProjectUtil.getMajMinVersion("spring-batch-core-2.4.0-M4");
assertEquals("2.4", version);
version = SpringProjectUtil.getMajMinVersion("spring-boot-4.4.0-RC2");
assertEquals("4.4", version);
version = SpringProjectUtil.getMajMinVersion("spring-integration-70.811.0.RELEASE");
assertEquals("70.811", version);
version = SpringProjectUtil.getMajMinVersion("another-java-");
assertNull(version);
version = SpringProjectUtil.getMajMinVersion("spring-core-5.f.2");
assertNull(version);
version = SpringProjectUtil.getMajMinVersion("springcore.f.b");
assertNull(version);
}
@Test
public void testVersionParsing() throws Exception {
String version = SpringProjectUtil.getVersion("spring-boot-starter-batch-2.3.0.RELEASE");
assertEquals("2.3.0.RELEASE", version);
version = SpringProjectUtil.getVersion("spring-batch-core-2.4.0-M4");
assertEquals("2.4.0-M4", version);
version = SpringProjectUtil.getVersion("spring-batch-core-2.4.0");
assertEquals("2.4.0", version);
version = SpringProjectUtil.getVersion("spring-boot-4.4.0-RC2");
assertEquals("4.4.0-RC2", version);
version = SpringProjectUtil.getVersion("spring-integration-70.811.0.RELEASE");
assertEquals("70.811.0.RELEASE", version);
version = SpringProjectUtil.getVersion("another-java-");
assertNull(version);
version = SpringProjectUtil.getVersion("spring-core-5.f.2");
assertNull(version);
version = SpringProjectUtil.getVersion("springcore.f.b");
assertNull(version);
}
@Test
public void testProjectSlugParsing() throws Exception {
String slug = SpringProjectUtil.getProjectSlug("spring-batch-core-2.4.0-M4");
assertEquals("spring-batch-core", slug);
slug = SpringProjectUtil.getProjectSlug("spring-2.4.0-M4");
assertEquals("spring", slug);
slug = SpringProjectUtil.getProjectSlug("-4.4.0-RC2");
assertNull(slug);
}
@Test
public void testVersionAndLibsFromActualProject() throws Exception {
IJavaProject jp = projects.mavenProject("empty-boot-1.3.0-app");
assertTrue(SpringProjectUtil.isBootProject(jp));
List<File> springLibs = SpringProjectUtil.getLibrariesOnClasspath(jp, "spring");
assertNotNull(springLibs);
assertTrue(springLibs.size() > 1);
File file = getLib(springLibs, "spring-boot");
assertNotNull(file);
assertTrue(file.exists());
String version = SpringProjectUtil.getMajMinVersion(file.getName());
assertEquals("1.3", version);
String slug = SpringProjectUtil.getProjectSlug(file.getName());
assertEquals("spring-boot", slug);
}
@Test
public void testProjectsInfoFromSpringIo() throws Exception {
@@ -195,22 +105,6 @@ public class ProjectGenerationsValidationTest {
assertEquals("2021-01-01", generation.getCommercialSupportEndDate());
}
@Test
public void testWarningsFromSample() throws Exception {
IJavaProject jp = projects.mavenProject("empty-boot-1.3.0-app");
SpringProjectsValidations validation = new SpringProjectsValidations(harness.getServer(),
ImmutableList.of( new SampleProjectsProvider())
);
ProjectValidation versionValidation = validation.validateVersion(jp);
assertNotNull(versionValidation != null);
assertEquals(versionValidation.getMessageType(), MessageType.Warning);
// Check that the message mentions the boot version of the project and the OSS support end date
assertEquals("Using spring-boot version: 1.3.2.RELEASE - OSS has ended on: 2020-01-01 - Commercial support has ended on: 2021-01-01", versionValidation.getMessage());
}
@Test
public void testDependencyVersionCalculation() throws Exception {
Version version = SpringProjectUtil.getDependencyVersion("spring-boot-1.2.3.jar", "spring-boot");
@@ -240,26 +134,4 @@ public class ProjectGenerationsValidationTest {
version = SpringProjectUtil.getDependencyVersion("spring-boot-actuator-1.2.3.BUILD-SNAPSHOT.jar", "spring-boot");
assertNull(version);
}
/*
*
*
* Helper methods
*
*
*
*/
private File getLib(List<File> springLibs, String slug) {
for (File file : springLibs) {
String name = file.getName();
String libSlug = SpringProjectUtil.getProjectSlug(name);
if (slug.equals(libSlug)) {
return file;
}
}
return null;
}
}