Various changes to version validation

Refactored version validation into separate validators that can each
compute its own severity, enablement and version to upgrade.
This facilitates reading of preferences.
Cleaned up other API.
Finished implementing releases support.
This commit is contained in:
Nieraj Singh
2022-11-13 13:17:35 -08:00
parent 422b14a763
commit 83d61f1a8f
22 changed files with 623 additions and 440 deletions

View File

@@ -15,11 +15,12 @@ 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.ProjectVersionDiagnosticProvider;
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.boot.validation.generations.VersionValidators;
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;
@@ -42,13 +43,14 @@ public class BootVersionValidator {
@Override
public void created(IJavaProject project) {
String url = "https://spring.io/api/projects";
String url = getSpringProjectsUrl();
SpringProjectsClient client = new SpringProjectsClient(url);
SpringProjectsProvider provider = new SpringIoProjectsProvider(client);
VersionValidators validators = new VersionValidators();
ProjectVersionDiagnosticProvider diagnosticProvider = new ProjectVersionDiagnosticProvider(provider, validators);
SpringBootProjectValidations validations = new SpringBootProjectValidations(provider);
try {
List<SpringProjectDiagnostic> diagnostics = validations.validateBootVersion(project);
List<SpringProjectDiagnostic> diagnostics = diagnosticProvider.getDiagnostics(project);
if (diagnostics != null) {
for (SpringProjectDiagnostic springProjectDiagnostic : diagnostics) {
server.getTextDocumentService().publishDiagnostics(new TextDocumentIdentifier(springProjectDiagnostic.getUri().toString()), List.of(springProjectDiagnostic.getDiagnostic()));
@@ -66,5 +68,11 @@ public class BootVersionValidator {
}
});
}
private String getSpringProjectsUrl() {
// TODO: Read from preferences
return "https://spring.io/api/projects";
}
}

View File

@@ -1,58 +0,0 @@
/*******************************************************************************
* 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";
private static final String[] BUILD_FILES = new String[] {"pom.xml", "build.gradle", "build.gradle.kts"};
/**
*
* @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 {
File file = null;
for (String fileName : BUILD_FILES) {
file = SpringProjectUtil.getFile(javaProject, fileName);
if (file != null) {
return file.toURI();
}
}
return null;
}
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

@@ -0,0 +1,172 @@
/*******************************************************************************
* 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.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.openrewrite.java.tree.J.If;
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.commons.java.IJavaProject;
import org.springframework.ide.vscode.commons.java.SpringProjectUtil;
import org.springframework.ide.vscode.commons.java.Version;
import com.google.common.collect.ImmutableList;
public class ProjectVersionDiagnosticProvider {
public static final String BOOT_VERSION_VALIDATION_CODE = "BOOT_VERSION_VALIDATION_CODE";
private static final String[] BUILD_FILES = new String[] { "pom.xml", "build.gradle", "build.gradle.kts" };
private static final String SPRING_BOOT_PROJECT_SLUG = "spring-boot";
private final SpringProjectsProvider provider;
private final VersionValidators validators;
public ProjectVersionDiagnosticProvider(SpringProjectsProvider provider, VersionValidators validationConditions) {
this.provider = provider;
this.validators = validationConditions;
}
/**
*
* @return Non-null list of Diagnostics. Can be empty if no diagnostics are
* applicable.
* @throws If error encountered while getting diagnostics
*/
public List<SpringProjectDiagnostic> getDiagnostics(IJavaProject javaProject) throws Exception {
URI uri = getBuildFileUri(javaProject);
if (uri == null) {
return ImmutableList.of();
}
ResolvedSpringProject springProject = provider.getProject(SPRING_BOOT_PROJECT_SLUG);
Version javaProjectVersion = SpringProjectUtil.getDependencyVersion(javaProject, springProject.getSlug());
if (javaProjectVersion == null) {
throw new Exception("Unable to resolve version for project: " + javaProject.getLocationUri().toString());
}
// The generation of the current dependency
Generation javaProjectGeneration = getGenerationForJavaProject(javaProject, springProject);
if (javaProjectGeneration == null) {
throw new Exception("Unable to find Spring Generation for project: " + javaProjectVersion.toString());
}
VersionValidation validation = null;
for (VersionValidator validator : validators.getValidators()) {
validation = validator.getValidation(springProject, javaProjectGeneration, javaProjectVersion);
if (validation != null) {
break;
}
}
if (validation != null) {
DiagnosticSeverity severity = validation.getSeverity();
Version toUpgrade = validation.getVersionToUprade();
StringBuffer msg = new StringBuffer();
msg.append(validation.getMessage());
if (toUpgrade != null) {
msg.append('\n');
msg.append("Consider upgrading to a newer supported version: ");
msg.append(toUpgrade.toString());
}
Diagnostic diagnostic = new Diagnostic();
diagnostic.setCode(BOOT_VERSION_VALIDATION_CODE);
diagnostic.setMessage(msg.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 ImmutableList.of(new SpringProjectDiagnostic(diagnostic, uri));
}
return ImmutableList.of();
}
private Generation getGenerationForJavaProject(IJavaProject javaProject, ResolvedSpringProject springProject)
throws Exception {
List<Generation> genList = springProject.getGenerations();
Version javaProjectVersion = SpringProjectUtil.getDependencyVersion(javaProject, springProject.getSlug());
// Find the generation belonging to the dependency
for (Generation gen : genList) {
Version genVersion = getVersion(gen);
if (genVersion.getMajor() == javaProjectVersion.getMajor()
&& genVersion.getMinor() == javaProjectVersion.getMinor()) {
return gen;
}
}
return null;
}
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);
}
protected URI getBuildFileUri(IJavaProject javaProject) throws Exception {
File file = null;
for (String fileName : BUILD_FILES) {
file = SpringProjectUtil.getFile(javaProject, fileName);
if (file != null) {
return file.toURI();
}
}
return null;
}
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,50 +0,0 @@
/*******************************************************************************
* 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

@@ -1,45 +0,0 @@
/*******************************************************************************
* Copyright (c) 2021, 2022 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.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.
*
* For example, given a spring dependency name: "spring-boot-2.4.0-M4" , the slug is
* "spring-boot", the fullVersion "2.4.0-M4", and the majMin "2.4"
*
*/
public class SpringDependencyInfo {
private final String slug;
private final Version version;
/**
*
* @param file spring for dependency, e.g. spring-boot-2.4.0-M4.jar
*/
public SpringDependencyInfo(IJavaProject project, String slug) {
this.slug = slug;
this.version = SpringProjectUtil.getDependencyVersion(project, slug);
}
public String getSlug() {
return slug;
}
public Version getVersion() {
return version;
}
}

View File

@@ -13,7 +13,7 @@ package org.springframework.ide.vscode.boot.validation.generations;
import java.util.List;
import java.util.Map;
import org.springframework.ide.vscode.boot.validation.generations.json.Generations;
import org.springframework.ide.vscode.boot.validation.generations.json.ResolvedSpringProject;
import org.springframework.ide.vscode.boot.validation.generations.json.SpringProject;
import org.springframework.ide.vscode.boot.validation.generations.json.SpringProjects;
@@ -21,24 +21,21 @@ import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMap.Builder;
/**
* Provides Spring project definitions from a source like "https://spring.io/api/projects"
* Provides Spring project definitions from a source like
* "https://spring.io/api/projects"
* <p/>
* If a client is not provided, a default client
* will be used instead that will point to "https://spring.io/api/projects"
* If a client is not provided, a default client will be used instead that will
* point to "https://spring.io/api/projects"
*
*/
public class SpringIoProjectsProvider implements SpringProjectsProvider {
private final SpringProjectsClient client;
private Map<String, SpringProject> cache;
private Map<String, ResolvedSpringProject> cache;
public SpringIoProjectsProvider(SpringProjectsClient client) {
this.client = client;
}
public SpringIoProjectsProvider() {
this(getDefaultClient());
}
/**
*
@@ -47,21 +44,12 @@ public class SpringIoProjectsProvider implements SpringProjectsProvider {
* @throws Exception
*/
@Override
public SpringProject getProject(String projectSlug) throws Exception {
SpringProject prj = cache().get(projectSlug);
public ResolvedSpringProject getProject(String projectSlug) throws Exception {
ResolvedSpringProject prj = cache().get(projectSlug);
return prj;
}
@Override
public Generations getGenerations(String projectSlug) throws Exception {
SpringProject project = cache().get(projectSlug);
if (project != null) {
return project.getGenerations(client);
}
return null;
}
private Map<String, SpringProject> cache() throws Exception {
private Map<String, ResolvedSpringProject> cache() throws Exception {
if (cache == null) {
SpringProjects springProjects = client.getSpringProjects();
cache = asMap(springProjects);
@@ -69,23 +57,18 @@ public class SpringIoProjectsProvider implements SpringProjectsProvider {
return cache != null ? cache : ImmutableMap.of();
}
private Map<String, SpringProject> asMap(SpringProjects springProjects) {
Builder<String, SpringProject> builder = ImmutableMap.builder();
private Map<String, ResolvedSpringProject> asMap(SpringProjects springProjects) {
Builder<String, ResolvedSpringProject> builder = ImmutableMap.builder();
if (springProjects != null) {
List<SpringProject> projects = springProjects.getProjects();
if (projects != null) {
for (SpringProject project : projects) {
builder.put(project.getSlug(), project);
builder.put(project.getSlug(), new ResolvedSpringProject(project, client));
}
}
}
return builder.build();
}
private static SpringProjectsClient getDefaultClient() {
String url = "https://spring.io/api/projects";
return new SpringProjectsClient(url);
}
}

View File

@@ -18,6 +18,7 @@ import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.ide.vscode.boot.validation.generations.json.Generations;
import org.springframework.ide.vscode.boot.validation.generations.json.Releases;
import org.springframework.ide.vscode.boot.validation.generations.json.SpringProjects;
import org.springframework.web.client.RestTemplate;
@@ -39,6 +40,10 @@ public class SpringProjectsClient {
return fromEmbedded(generationsUrl, Generations.class);
}
public Releases getReleases(String releasesUrl) throws Exception {
return fromEmbedded(releasesUrl, Releases.class);
}
private <T> T fromEmbedded(String url, Class<T> clazz) throws Exception {
if (url != null) {
Map<?, ?> result = get(url, Map.class);

View File

@@ -10,8 +10,7 @@
*******************************************************************************/
package org.springframework.ide.vscode.boot.validation.generations;
import org.springframework.ide.vscode.boot.validation.generations.json.Generations;
import org.springframework.ide.vscode.boot.validation.generations.json.SpringProject;
import org.springframework.ide.vscode.boot.validation.generations.json.ResolvedSpringProject;
public interface SpringProjectsProvider {
@@ -21,8 +20,6 @@ public interface SpringProjectsProvider {
* @return
* @throws Exception
*/
SpringProject getProject(String projectSlug) throws Exception;
Generations getGenerations(String projectSlug) throws Exception;
ResolvedSpringProject getProject(String projectSlug) throws Exception;
}

View File

@@ -1,193 +0,0 @@
/*******************************************************************************
* 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

@@ -0,0 +1,46 @@
/*******************************************************************************
* 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 org.eclipse.lsp4j.DiagnosticSeverity;
import org.springframework.ide.vscode.commons.java.Version;
public class VersionValidation {
private final Version versionToUpgrade;
private final boolean enabled;
private final DiagnosticSeverity severity;
private final String message;
public VersionValidation(Version versionToUpgrade, boolean enabled, DiagnosticSeverity severity, String message) {
this.versionToUpgrade = versionToUpgrade;
this.enabled = enabled;
this.severity = severity;
this.message = message;
}
public boolean isEnabled() {
return this.enabled;
}
public DiagnosticSeverity getSeverity() {
return this.severity;
}
public Version getVersionToUprade() {
return this.versionToUpgrade;
}
public String getMessage() {
return this.message;
}
}

View File

@@ -0,0 +1,61 @@
/*******************************************************************************
* 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.sql.Date;
import java.util.List;
import org.springframework.ide.vscode.boot.validation.generations.json.Generation;
import org.springframework.ide.vscode.boot.validation.generations.json.Release;
import org.springframework.ide.vscode.boot.validation.generations.json.ResolvedSpringProject;
import org.springframework.ide.vscode.commons.java.Version;
public class VersionValidationUtils {
private static final String GENERAL_AVAILABILITY_STATUS = "GENERAL_AVAILABILITY";
public static boolean isOssValid(Generation gen) {
Date currentDate = new Date(System.currentTimeMillis());
Date ossEndDate = Date.valueOf(gen.getOssSupportEndDate());
return currentDate.before(ossEndDate);
}
public static boolean isCommercialValid(Generation gen) {
Date currentDate = new Date(System.currentTimeMillis());
Date commercialEndDate = Date.valueOf(gen.getCommercialSupportEndDate());
return currentDate.before(commercialEndDate);
}
public static Version getLatestSupportedInSameMajor(ResolvedSpringProject springProject, Version version)
throws Exception {
List<Release> rls = springProject.getReleases();
for (Release release : rls) {
Version rlVersion = release.getVersion();
if (GENERAL_AVAILABILITY_STATUS.equals(release.getStatus())
&& rlVersion.getMajor() == version.getMajor()) {
return rlVersion;
}
}
return null;
}
public static Version getLatestSupportedRelease(ResolvedSpringProject springProject, Version version)
throws Exception {
List<Release> rls = springProject.getReleases();
for (Release release : rls) {
if (release.isCurrent()) {
return release.getVersion();
}
}
return null;
}
}

View File

@@ -0,0 +1,31 @@
/*******************************************************************************
* 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 org.springframework.ide.vscode.boot.validation.generations.json.Generation;
import org.springframework.ide.vscode.boot.validation.generations.json.ResolvedSpringProject;
import org.springframework.ide.vscode.commons.java.Version;
public interface VersionValidator {
/**
*
* @param springProject contains information about the spring project associated
* with the generation and version to validate
* @param generation to validate
* @param version to validate
* @return validation if application. Null otherwise
* @throws Exception
*/
VersionValidation getValidation(ResolvedSpringProject springProject, Generation generation, Version version)
throws Exception;
}

View File

@@ -0,0 +1,132 @@
/*******************************************************************************
* 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.util.List;
import org.eclipse.lsp4j.DiagnosticSeverity;
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.commons.java.Version;
import com.google.common.collect.ImmutableList;
public class VersionValidators {
public List<VersionValidator> getValidators() {
return ImmutableList.of(new SupportedValidator(), new UnsupportedCommercialValidator(),
new UnsupportedOssValidator(), new UnsupportedValidator());
}
private static class SupportedValidator implements VersionValidator {
@Override
public VersionValidation getValidation(ResolvedSpringProject springProject, Generation generation,
Version version) throws Exception {
if (VersionValidationUtils.isCommercialValid(generation) && VersionValidationUtils.isOssValid(generation)) {
DiagnosticSeverity severity = DiagnosticSeverity.Information;
boolean enabled = true;
StringBuffer message = new StringBuffer();
message.append("OSS support ends on: ");
message.append(generation.getOssSupportEndDate());
message.append('\n');
message.append("Commercial supports ends on: ");
message.append(generation.getCommercialSupportEndDate());
Version toUpdate = VersionValidationUtils.getLatestSupportedRelease(springProject, version);
return new VersionValidation(toUpdate, enabled, severity, message.toString());
}
return null;
}
}
private static class UnsupportedValidator implements VersionValidator {
@Override
public VersionValidation getValidation(ResolvedSpringProject springProject, Generation generation,
Version version) throws Exception {
if (!VersionValidationUtils.isCommercialValid(generation)
&& !VersionValidationUtils.isOssValid(generation)) {
DiagnosticSeverity severity = DiagnosticSeverity.Error;
boolean enabled = true;
StringBuffer message = new StringBuffer();
message.append("Unsupported OSS. Support ended on: ");
message.append(generation.getOssSupportEndDate());
message.append('\n');
message.append("Unsupported Commercial. Support ended on: ");
message.append(generation.getCommercialSupportEndDate());
return new VersionValidation(
toUpdateForUnsupported(springProject, version), enabled, severity, message.toString());
}
return null;
}
}
private static class UnsupportedOssValidator implements VersionValidator {
@Override
public VersionValidation getValidation(ResolvedSpringProject springProject, Generation generation,
Version version) throws Exception {
if (!VersionValidationUtils.isOssValid(generation)
&& VersionValidationUtils.isCommercialValid(generation)) {
DiagnosticSeverity severity = DiagnosticSeverity.Warning;
boolean enabled = true;
StringBuffer message = new StringBuffer();
message.append("Unsupported OSS. Support ended on: ");
message.append(generation.getOssSupportEndDate());
message.append('\n');
message.append("Commercial supports ends on: ");
message.append(generation.getCommercialSupportEndDate());
return new VersionValidation(toUpdateForUnsupported(springProject, version), enabled, severity, message.toString());
}
return null;
}
}
private static class UnsupportedCommercialValidator implements VersionValidator {
@Override
public VersionValidation getValidation(ResolvedSpringProject springProject, Generation generation,
Version version) throws Exception {
if (!VersionValidationUtils.isCommercialValid(generation)
&& VersionValidationUtils.isOssValid(generation)) {
DiagnosticSeverity severity = DiagnosticSeverity.Warning;
boolean enabled = true;
StringBuffer message = new StringBuffer();
message.append("OSS support ends on: ");
message.append(generation.getOssSupportEndDate());
message.append('\n');
message.append("Unsupported Commercial. Support ended on: ");
message.append(generation.getCommercialSupportEndDate());
return new VersionValidation(toUpdateForUnsupported(springProject, version), enabled, severity, message.toString());
}
return null;
}
}
private static Version toUpdateForUnsupported(ResolvedSpringProject springProject, Version version)
throws Exception {
Version toUpdate = VersionValidationUtils.getLatestSupportedInSameMajor(springProject, version);
if (toUpdate == null) {
toUpdate = VersionValidationUtils.getLatestSupportedRelease(springProject, version);
}
return toUpdate;
}
}

View File

@@ -17,4 +17,8 @@ public class JsonHalLinks {
public Links get_links() {
return _links;
}
protected void set_links(Links links) {
this._links = links;
}
}

View File

@@ -17,6 +17,7 @@ public class Links {
private Link generations;
private Link parent;
private Link project;
private Link repository;
public Link getSelf() {
return self;
@@ -37,4 +38,8 @@ public class Links {
public Link getProject() {
return project;
}
public Link getRepository() {
return repository;
}
}

View File

@@ -1,13 +1,26 @@
/*******************************************************************************
* 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.json;
import org.springframework.ide.vscode.commons.java.SpringProjectUtil;
import org.springframework.ide.vscode.commons.java.Version;
public class Release {
public class Release extends JsonHalLinks {
private String version;
private String status;
private boolean current;
private String referenceDocUrl;
private String apiDocUrl;
public Version getVersion() {
return SpringProjectUtil.getVersion(version);
@@ -20,4 +33,13 @@ public class Release {
public boolean isCurrent() {
return current;
}
public String getReferenceDocUrl() {
return referenceDocUrl;
}
public String getApiDocUrl() {
return apiDocUrl;
}
}

View File

@@ -1,3 +1,13 @@
/*******************************************************************************
* 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.json;
import java.util.Arrays;

View File

@@ -0,0 +1,61 @@
/*******************************************************************************
* 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.json;
import java.util.List;
import org.springframework.ide.vscode.boot.validation.generations.SpringProjectsClient;
import com.google.common.collect.ImmutableList;
public class ResolvedSpringProject extends SpringProject {
private final SpringProjectsClient client;
private Generations generations;
private Releases releases;
public ResolvedSpringProject(SpringProject project, SpringProjectsClient client) {
this.client = client;
setName(project.getName());
setRepositoryUrl(project.getRepositoryUrl());
setSlug(project.getSlug());
setStatus(project.getStatus());
set_links(project.get_links());
}
public List<Generation> getGenerations() throws Exception {
// cache the generations to prevent frequent calls to the client
if (this.generations == null) {
Links _links = get_links();
if (_links != null) {
Link genLink = _links.getGenerations();
if (genLink != null) {
this.generations = client.getGenerations(genLink.getHref());
}
}
}
return this.generations != null ? this.generations.getGenerations() : ImmutableList.of();
}
public List<Release> getReleases() throws Exception {
// cache the releases to prevent frequent calls to the client
if (this.releases == null) {
Links _links = get_links();
if (_links != null) {
Link genLink = _links.getReleases();
if (genLink != null) {
this.releases = client.getReleases(genLink.getHref());
}
}
}
return this.releases != null ? this.releases.getReleases() : ImmutableList.of();
}
}

View File

@@ -10,14 +10,8 @@
*******************************************************************************/
package org.springframework.ide.vscode.boot.validation.generations.json;
import org.springframework.ide.vscode.boot.validation.generations.SpringProjectsClient;
public class SpringProject extends JsonHalLinks {
private Generations generations;
private Releases releases;
private String name;
private String slug;
private String status;
@@ -31,7 +25,6 @@ public class SpringProject extends JsonHalLinks {
return slug;
}
public String getStatus() {
return status;
}
@@ -40,31 +33,19 @@ public class SpringProject extends JsonHalLinks {
return repositoryUrl;
}
public Generations getGenerations(SpringProjectsClient client) throws Exception {
// cache the generations to prevent frequent calls to the client
if (this.generations == null) {
Links _links = get_links();
if (_links != null) {
Link genLink = _links.getGenerations();
if (genLink != null) {
this.generations = client.getGenerations(genLink.getHref());
}
}
}
return this.generations;
protected void setName(String name) {
this.name = name;
}
public Releases getReleases(SpringProjectsClient client) throws Exception {
// cache the releases to prevent frequent calls to the client
if (this.releases == null) {
Links _links = get_links();
if (_links != null) {
Link genLink = _links.getReleases();
if (genLink != null) {
// this.releases = client.getGenerations(genLink.getHref());
}
}
}
return this.releases;
protected void setSlug(String slug) {
this.slug = slug;
}
protected void setStatus(String status) {
this.status = status;
}
protected void setRepositoryUrl(String repositoryUrl) {
this.repositoryUrl = repositoryUrl;
}
}

View File

@@ -13,6 +13,7 @@ package org.springframework.ide.vscode.boot.validation.generations.json;
import java.util.List;
public class SpringProjects {
private List<SpringProject> projects;
public List<SpringProject> getProjects() {

View File

@@ -28,8 +28,8 @@ import org.springframework.ide.vscode.boot.validation.generations.SpringIoProjec
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.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.ResolvedSpringProject;
import org.springframework.ide.vscode.boot.validation.generations.json.SpringProject;
import org.springframework.ide.vscode.commons.java.SpringProjectUtil;
import org.springframework.ide.vscode.commons.java.Version;
@@ -86,13 +86,10 @@ public class ProjectGenerationsValidationTest {
SampleProjectsProvider provider = new SampleProjectsProvider();
SpringProject project = provider.getProject("spring-boot");
ResolvedSpringProject project = provider.getProject("spring-boot");
assertNotNull(project);
Generations generations = provider.getGenerations("spring-boot");
assertNotNull(generations);
List<Generation> genList = generations.getGenerations();
List<Generation> genList = project.getGenerations();
assertNotNull(genList);
assertTrue(genList.size() > 0);

View File

@@ -14,25 +14,48 @@ import java.util.List;
import java.util.Map;
import org.springframework.ide.vscode.boot.validation.generations.SpringProjectsProvider;
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.Release;
import org.springframework.ide.vscode.boot.validation.generations.json.ResolvedSpringProject;
import org.springframework.ide.vscode.boot.validation.generations.json.SpringProject;
import org.springframework.ide.vscode.boot.validation.generations.json.SpringProjects;
import com.fasterxml.jackson.databind.ObjectMapper;
/**
* Spring-boot sample json. Used for testing or providing a hardcoded fall-back to spring.io, when
* the latter is not available.
*
* Spring-boot sample json. Used for testing or providing a hardcoded fall-back
* to spring.io, when the latter is not available.
*
*/
public class SampleProjectsProvider implements SpringProjectsProvider {
private SpringProjects projects;
@Override
public SpringProject getProject(String projectSlug) throws Exception {
public ResolvedSpringProject getProject(String projectSlug) throws Exception {
ResolvedSpringProject project = new ResolvedSpringProject(getSpringProject(projectSlug), null) {
@Override
public List<Generation> getGenerations() throws Exception {
SpringProject project = getSpringProject(projectSlug);
if (project != null && project.getSlug().equals("spring-boot")) {
return parse(SPRING_BOOT_PROJECT_GENERATIONS, Generations.class).getGenerations();
}
return null;
}
@Override
public List<Release> getReleases() throws Exception {
return null;
}
};
return project;
}
private SpringProject getSpringProject(String projectSlug) throws Exception {
if (this.projects == null) {
this.projects = parse(SPRING_PROJECTS_JSON_SAMPLE, SpringProjects.class);
}
@@ -45,15 +68,6 @@ public class SampleProjectsProvider implements SpringProjectsProvider {
return null;
}
@Override
public Generations getGenerations(String projectSlug) throws Exception {
SpringProject project = getProject(projectSlug);
if (project != null && project.getSlug().equals("spring-boot")) {
return parse(SPRING_BOOT_PROJECT_GENERATIONS, Generations.class);
}
return null;
}
private <T> T parse(String json, Class<T> clazz) throws Exception {
ObjectMapper mapper = new ObjectMapper();
@@ -112,5 +126,4 @@ public class SampleProjectsProvider implements SpringProjectsProvider {
+ " },\n" + " \"parent\" : {\n"
+ " \"href\" : \"https://spring.io/api/projects/spring-data\"\n" + " }\n" + " }\n"
+ " } ]\n" + " }\n" + "}";
}