PT 173730396 - Further changes for project version validation

This commit is contained in:
Nieraj Singh
2021-01-26 00:29:15 -08:00
parent 909b8f4b8f
commit 9384f71223
6 changed files with 172 additions and 68 deletions

View File

@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2018, 2020 Pivotal, Inc.
* Copyright (c) 2018, 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
@@ -15,8 +15,6 @@ import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
import org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration;
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
@@ -45,10 +43,11 @@ import org.springframework.ide.vscode.boot.metadata.ClassReferenceProvider;
import org.springframework.ide.vscode.boot.metadata.LoggerNameProvider;
import org.springframework.ide.vscode.boot.metadata.ProjectBasedPropertyIndexProvider;
import org.springframework.ide.vscode.boot.metadata.SpringPropertyIndex;
import org.springframework.ide.vscode.boot.metadata.SpringPropertyIndexProvider;
import org.springframework.ide.vscode.boot.metadata.ValueProviderRegistry;
import org.springframework.ide.vscode.boot.metadata.types.TypeUtilProvider;
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;
@@ -72,6 +71,8 @@ import org.springframework.ide.vscode.languageserver.starter.LanguageServerAutoC
import org.springframework.ide.vscode.languageserver.starter.LanguageServerRunnerAutoConf;
import org.yaml.snakeyaml.Yaml;
import com.google.common.collect.ImmutableList;
import reactor.core.publisher.Hooks;
@SpringBootConfiguration(proxyBeanMethods = false)
@@ -215,5 +216,11 @@ public class BootLanguagServerBootApp {
}
};
}
@Bean SpringProjectsValidations springProjectsValidations(SimpleLanguageServer server) {
return new SpringProjectsValidations(server, ImmutableList.of(
new SpringIoProjectsProvider(),
new SampleProjectsProvider()
));
}
}

View File

@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2018, 2019 Pivotal, Inc.
* Copyright (c) 2018, 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
@@ -13,6 +13,7 @@ package org.springframework.ide.vscode.boot.app;
import java.util.List;
import java.util.Optional;
import org.eclipse.lsp4j.MessageType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
@@ -26,7 +27,11 @@ 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;
@@ -34,6 +39,7 @@ import org.springframework.ide.vscode.commons.languageserver.composable.Composit
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.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;
@@ -59,6 +65,7 @@ public class BootLanguageServerInitializer implements InitializingBean {
@Autowired BootJavaConfig config;
@Autowired SpringSymbolIndex springIndexer;
@Autowired(required = false) List<ICompletionEngine> completionEngines;
@Autowired SpringProjectsValidations springProjectsValidations;
@Qualifier("adHocProperties") @Autowired ProjectBasedPropertyIndexProvider adHocProperties;
@@ -120,6 +127,8 @@ public class BootLanguageServerInitializer implements InitializingBean {
}
});
});
addSpringProjectsVersionValidation(params);
}
public CompositeLanguageServerComponents getComponents() {
@@ -132,5 +141,50 @@ public class BootLanguageServerInitializer implements InitializingBean {
completionEngineAdapter.setMaxCompletions(number);
}
}
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);
}
}
});
}
}

View File

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

@@ -25,14 +25,14 @@ import com.google.common.collect.ImmutableMap.Builder;
/**
* Provides Spring project definitions from a source like "https://spring.io/api/projects"
* <p/>
* If a client is not provider that can fetch information from a source, a default client
* 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, SpringIoProject> cache;
public SpringIoProjectsProvider(SpringProjectsClient client) {
this.client = client;
@@ -50,25 +50,20 @@ public class SpringIoProjectsProvider implements SpringProjectsProvider {
*/
@Override
public SpringProject getProject(String projectSlug) throws Exception {
return cache().get(projectSlug);
SpringIoProject prj = cache().get(projectSlug);
return prj != null ? prj.getProject() : null;
}
@Override
public Generations getGenerations(String projectSlug) throws Exception {
SpringProject project = getProject(projectSlug);
SpringIoProject project = cache().get(projectSlug);
if (project != null) {
Links _links = project.get_links();
if (_links != null) {
Link genLink = _links.getGenerations();
if (genLink != null) {
return client.getGenerations(genLink.getHref());
}
}
return project.getGenerations();
}
return null;
}
private Map<String, SpringProject> cache() throws Exception {
private Map<String, SpringIoProject> cache() throws Exception {
if (cache == null) {
SpringProjects springProjects = client.getSpringProjects();
cache = asMap(springProjects);
@@ -76,14 +71,14 @@ 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, SpringIoProject> asMap(SpringProjects springProjects) {
Builder<String, SpringIoProject> 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 SpringIoProject(project, this.client));
}
}
}
@@ -94,4 +89,39 @@ public class SpringIoProjectsProvider implements SpringProjectsProvider {
String url = "https://spring.io/api/projects";
return new SpringProjectsClient(url);
}
/**
* Wrapper around the JSON SpringProject that also contains its generations
*
*/
private final static class SpringIoProject {
private final SpringProject project;
private final SpringProjectsClient client;
private Generations generations;
public SpringIoProject(SpringProject project, SpringProjectsClient client) {
this.project = project;
this.client = client;
}
public SpringProject getProject() {
return this.project;
}
public Generations getGenerations() throws Exception {
// cache the generations to prevent frequent calls to the client
if (this.generations == null) {
Links _links = project.get_links();
if (_links != null) {
Link genLink = _links.getGenerations();
if (genLink != null) {
this.generations = client.getGenerations(genLink.getHref());
}
}
}
return this.generations;
}
}
}

View File

@@ -12,26 +12,17 @@ package org.springframework.ide.vscode.boot.validation.generations;
import java.io.File;
import java.sql.Date;
import java.time.Duration;
import java.util.List;
import java.util.concurrent.CompletableFuture;
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;
import org.springframework.ide.vscode.commons.util.AsyncRunner;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableList.Builder;
import reactor.core.publisher.Mono;
public class SpringProjectsValidations {
private static final long TIMEOUT_SECS = 30;
private final List<SpringProjectsProvider> projectsProviders;
private final SimpleLanguageServer server;
@@ -39,22 +30,9 @@ public class SpringProjectsValidations {
this.projectsProviders = projectsProviders;
this.server = server;
}
public CompletableFuture<List<String>> getValidationMessagesAsync(IJavaProject jp) {
AsyncRunner async = this.server.getAsync();
if (async != null) {
return async.invoke(Duration.ofSeconds(TIMEOUT_SECS), () -> {
return getWarningMessages(jp);
});
} else {
return Mono.fromCallable(() -> getWarningMessages(jp)).timeout(Duration.ofSeconds(TIMEOUT_SECS))
.toFuture();
}
}
public List<String> getWarningMessages(IJavaProject jp) throws Exception {
ImmutableList.Builder<String> messages = ImmutableList.builder();
public ProjectValidation validateVersion(IJavaProject jp) throws Exception {
StringBuilder builder = new StringBuilder();
if (jp != null) {
List<File> librariesOnClasspath = SpringProjectUtil.getLibrariesOnClasspath(jp, "spring");
if (librariesOnClasspath != null) {
@@ -66,7 +44,7 @@ public class SpringProjectsValidations {
List<Generation> gens = generations.getGenerations();
if (gens != null) {
for (Generation gen : gens) {
resolveWarnings(gen, messages, versionInfo);
resolveWarnings(gen, builder, versionInfo);
}
}
}
@@ -74,32 +52,31 @@ public class SpringProjectsValidations {
}
}
}
return messages.build();
return builder.length() > 0 ?
new ProjectValidation(builder.toString(), MessageType.Warning)
: ProjectValidation.OK;
}
private void resolveWarnings(Generation gen, Builder<String> messages, SpringVersionInfo versionInfo) {
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());
StringBuilder msg = new StringBuilder();
msg.append("Using ");
msg.append(versionInfo.getSlug());
msg.append(" version: ");
msg.append(versionInfo.getFullVersion());
messages.append("Using ");
messages.append(versionInfo.getSlug());
messages.append(" version: ");
messages.append(versionInfo.getFullVersion());
if (currentDate.after(ossEndDate)) {
msg.append(" - OSS has ended on: ");
msg.append(gen.getOssSupportEndDate());
messages.append(" - OSS has ended on: ");
messages.append(gen.getOssSupportEndDate());
}
if (currentDate.after(commercialEndDate)) {
msg.append(" - Commercial support has ended on: ");
msg.append(gen.getCommercialSupportEndDate());
messages.append(" - Commercial support has ended on: ");
messages.append(gen.getCommercialSupportEndDate());
}
messages.add(msg.toString());
}
}

View File

@@ -19,6 +19,7 @@ import java.io.File;
import java.sql.Date;
import java.util.List;
import org.eclipse.lsp4j.MessageType;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -26,6 +27,7 @@ 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;
@@ -198,11 +200,11 @@ public class ProjectGenerationsValidationTest {
ImmutableList.of( new SampleProjectsProvider())
);
List<String> messages = validation.getWarningMessages(jp);
assertTrue(messages != null && messages.size() > 0);
String msg = messages.get(0);
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 - OSS has ended on: 2020-01-01 - Commercial support has ended on: 2021-01-01", msg);
assertEquals("Using spring-boot version: 1.3.2 - OSS has ended on: 2020-01-01 - Commercial support has ended on: 2021-01-01", versionValidation.getMessage());
}
/*