From 4fe5be7711cb26b30ac35bce7d38018814d94fd9 Mon Sep 17 00:00:00 2001 From: aboyko Date: Wed, 11 Oct 2023 13:42:44 -0400 Subject: [PATCH] Single error message for SprinIO service failure. Re-work progress. --- .../boot/app/BootVersionValidationConfig.java | 8 +-- .../CachedErrorStateException.java | 21 ++++++++ .../ProjectVersionDiagnosticProvider.java | 48 ++++++----------- .../generations/SpringIoProjectsProvider.java | 53 +++++++++++++++++-- .../ProjectGenerationsValidationTest.java | 2 +- 5 files changed, 89 insertions(+), 43 deletions(-) create mode 100644 headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/validation/generations/CachedErrorStateException.java diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/BootVersionValidationConfig.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/BootVersionValidationConfig.java index 7f6add42b..22eea0b53 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/BootVersionValidationConfig.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/BootVersionValidationConfig.java @@ -42,16 +42,16 @@ public class BootVersionValidationConfig { return new UpdateBootVersion(server.getDiagnosticSeverityProvider(), bootUpgradeOpt, projectsProvider); } - @Bean SpringIoProjectsProvider springProjectsProvider(BootJavaConfig config, RestTemplateFactory restTemplateFactory) { - return new SpringIoProjectsProvider(config, restTemplateFactory); + @Bean SpringIoProjectsProvider springProjectsProvider(SimpleLanguageServer server, BootJavaConfig config, RestTemplateFactory restTemplateFactory) { + return new SpringIoProjectsProvider(config, restTemplateFactory, server.getProgressService(), server.getMessageService(), 30_000); } @Bean GenerationsValidator generationsValidator(SimpleLanguageServer server, SpringProjectsProvider projectsProvider) { return new GenerationsValidator(server.getDiagnosticSeverityProvider(), projectsProvider); } - @Bean ProjectVersionDiagnosticProvider projectVersionDiagnosticProvider(SimpleLanguageServer server, List validators) { - return new ProjectVersionDiagnosticProvider(server.getProgressService(), server.getMessageService(), validators); + @Bean ProjectVersionDiagnosticProvider projectVersionDiagnosticProvider(List validators) { + return new ProjectVersionDiagnosticProvider(validators); } @ConditionalOnMissingClass("org.springframework.ide.vscode.languageserver.testharness.LanguageServerHarness") diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/validation/generations/CachedErrorStateException.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/validation/generations/CachedErrorStateException.java new file mode 100644 index 000000000..40f2767d0 --- /dev/null +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/validation/generations/CachedErrorStateException.java @@ -0,0 +1,21 @@ +/******************************************************************************* + * Copyright (c) 2023 VMware, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * VMware, Inc. - initial API and implementation + *******************************************************************************/ +package org.springframework.ide.vscode.boot.validation.generations; + +public class CachedErrorStateException extends Exception { + + private static final long serialVersionUID = 1L; + + public CachedErrorStateException(Throwable t) { + super(t); + } + +} diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/validation/generations/ProjectVersionDiagnosticProvider.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/validation/generations/ProjectVersionDiagnosticProvider.java index e4fc3e281..5b48893fa 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/validation/generations/ProjectVersionDiagnosticProvider.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/validation/generations/ProjectVersionDiagnosticProvider.java @@ -24,9 +24,6 @@ import org.slf4j.LoggerFactory; import org.springframework.ide.vscode.commons.Version; import org.springframework.ide.vscode.commons.java.IJavaProject; import org.springframework.ide.vscode.commons.java.SpringProjectUtil; -import org.springframework.ide.vscode.commons.languageserver.MessageService; -import org.springframework.ide.vscode.commons.languageserver.PercentageProgressTask; -import org.springframework.ide.vscode.commons.languageserver.ProgressService; public class ProjectVersionDiagnosticProvider { @@ -34,13 +31,7 @@ public class ProjectVersionDiagnosticProvider { private final List validators; - private ProgressService progressService; - - private MessageService messageService; - - public ProjectVersionDiagnosticProvider(ProgressService progressService, MessageService messageService, List validators) { - this.progressService = progressService; - this.messageService = messageService; + public ProjectVersionDiagnosticProvider(List validators) { this.validators = validators; } @@ -57,33 +48,24 @@ public class ProjectVersionDiagnosticProvider { if (!applicableValidators.isEmpty()) { - PercentageProgressTask progress = progressService.createPercentageProgressTask( - "validate-" + javaProject.getElementName(), - applicableValidators.size(), - "Validating Spring Boot Version of project '%s'".formatted(javaProject.getElementName())); - - try { - Version javaProjectVersion = SpringProjectUtil.getSpringBootVersion(javaProject); + Version javaProjectVersion = SpringProjectUtil.getSpringBootVersion(javaProject); - if (javaProjectVersion == null) { - log.warn("Unable to resolve version for project: " + javaProject.getLocationUri().toASCIIString()); - return new DiagnosticResult(buildFileUri, Collections.emptyList()); - } + if (javaProjectVersion == null) { + log.warn("Unable to resolve version for project: " + javaProject.getLocationUri().toASCIIString()); + return new DiagnosticResult(buildFileUri, Collections.emptyList()); + } - for (VersionValidator validator : applicableValidators) { - try { - Collection batch = validator.validate(javaProject, javaProjectVersion); - if (batch != null) { - diagnostics.addAll(batch); - } - } catch (Exception e) { - messageService.error("Failed Spring Boot version validation for project '%s': %s".formatted(javaProject.getElementName(), e.getMessage())); - log.error("", e); + for (VersionValidator validator : applicableValidators) { + try { + Collection batch = validator.validate(javaProject, javaProjectVersion); + if (batch != null) { + diagnostics.addAll(batch); } - progress.increment(); + } catch (CachedErrorStateException e) { + // ignore the cached error state + } catch (Exception e) { + log.error("", e); } - } finally { - progress.done(); } } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/validation/generations/SpringIoProjectsProvider.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/validation/generations/SpringIoProjectsProvider.java index 5a2dc191a..025968332 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/validation/generations/SpringIoProjectsProvider.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/validation/generations/SpringIoProjectsProvider.java @@ -12,12 +12,16 @@ package org.springframework.ide.vscode.boot.validation.generations; import java.util.List; import java.util.Map; +import java.util.Optional; import org.springframework.ide.vscode.boot.app.BootJavaConfig; import org.springframework.ide.vscode.boot.app.RestTemplateFactory; 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 org.springframework.ide.vscode.commons.languageserver.IndefiniteProgressTask; +import org.springframework.ide.vscode.commons.languageserver.MessageService; +import org.springframework.ide.vscode.commons.languageserver.ProgressService; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap.Builder; @@ -31,13 +35,23 @@ import com.google.common.collect.ImmutableMap.Builder; * */ public class SpringIoProjectsProvider implements SpringProjectsProvider { - + private SpringProjectsClient client; private Map cache; private RestTemplateFactory restTemplateFactory; + final private ProgressService progressService; + final private MessageService messageService; + final private long errorStateCachingTime; + + private long lastErrorTime; + private Optional errorState; - public SpringIoProjectsProvider(BootJavaConfig config, RestTemplateFactory restTemplateFactory) { + public SpringIoProjectsProvider(BootJavaConfig config, RestTemplateFactory restTemplateFactory, ProgressService progressService, MessageService messageService, long errorStateCachingTime) { this.restTemplateFactory = restTemplateFactory; + this.progressService = progressService; + this.messageService = messageService; + this.errorStateCachingTime = errorStateCachingTime; + clearErrorState(); updateIoApiUri(config.getSpringIOApiUrl()); config.addListener(v -> updateIoApiUri(config.getSpringIOApiUrl())); } @@ -46,6 +60,7 @@ public class SpringIoProjectsProvider implements SpringProjectsProvider { if (client == null || !uri.equals(client.getUrl())) { this.client = new SpringProjectsClient(uri, restTemplateFactory); cache = null; + clearErrorState(); } } @@ -63,12 +78,40 @@ public class SpringIoProjectsProvider implements SpringProjectsProvider { private Map cache() throws Exception { if (cache == null) { - SpringProjects springProjects = client.getSpringProjects(); - cache = asMap(springProjects); + if (lastErrorTime + errorStateCachingTime < System.currentTimeMillis()) { + IndefiniteProgressTask progress = progressService.createIndefiniteProgressTask("fetching-from-spring-io", "Fetching Generations from Spring IO", null); + try { + SpringProjects springProjects = client.getSpringProjects(); + cache = asMap(springProjects); + // Wipe out error state + clearErrorState(); + } catch (Exception e) { + messageService.error("Failed to fetch Generation from Spring IO: %s".formatted(e.getMessage())); + setErrorState(e); + throw e; + } finally { + progress.done(); + } + } else { + // The error state hasn't expired - throw a cached error state exception + if (errorState.isPresent()) { + throw new CachedErrorStateException(errorState.get()); + } + } } return cache != null ? cache : ImmutableMap.of(); } - + + private void clearErrorState() { + errorState = Optional.empty(); + lastErrorTime = 0; + } + + private void setErrorState(Throwable t) { + errorState = Optional.of(t); + lastErrorTime = System.currentTimeMillis(); + } + private Map asMap(SpringProjects springProjects) { Builder builder = ImmutableMap.builder(); diff --git a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/validation/test/ProjectGenerationsValidationTest.java b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/validation/test/ProjectGenerationsValidationTest.java index 6289e3aef..dddce15d3 100644 --- a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/validation/test/ProjectGenerationsValidationTest.java +++ b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/validation/test/ProjectGenerationsValidationTest.java @@ -56,7 +56,7 @@ public class ProjectGenerationsValidationTest { @Test void testProjectsInfoFromSpringIo() throws Exception { - SpringProjectsProvider cache = new SpringIoProjectsProvider(config, restTemplateFactory); + SpringProjectsProvider cache = new SpringIoProjectsProvider(config, restTemplateFactory, harness.getServer().getProgressService(), harness.getServer().getMessageService(), -1); SpringProject project = cache.getProject("spring-boot"); assertNotNull(project);