diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/validation/generations/BootVersionsFromMavenCentral.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/validation/generations/BootVersionsFromMavenCentral.java index c43964da9..9d25ded83 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/validation/generations/BootVersionsFromMavenCentral.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/validation/generations/BootVersionsFromMavenCentral.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2022 VMware, Inc. + * Copyright (c) 2022, 2023 VMware, Inc. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -10,6 +10,7 @@ *******************************************************************************/ package org.springframework.ide.vscode.boot.validation.generations; +import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -20,6 +21,7 @@ import org.slf4j.LoggerFactory; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatusCode; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.ide.vscode.commons.java.SpringProjectUtil; @@ -32,21 +34,21 @@ public class BootVersionsFromMavenCentral { private static final String URL = "https://search.maven.org/solrsearch/select?q=g:org.springframework.boot+AND+a:spring-boot-starter-parent&core=gav&rows=200&wt=json"; @SuppressWarnings({ "rawtypes", "unchecked" }) - public static List getBootVersions() throws Exception { + public static List getBootVersions() throws IOException { HttpHeaders headers = new HttpHeaders(); headers.setAccept(MediaType.parseMediaTypes("application/json")); - HttpEntity entity = new HttpEntity(headers); + HttpEntity entity = new HttpEntity<>(headers); RestTemplate restTemplate = new RestTemplate(); log.info("search maven central for Spring Boot release information via: " + URL); ResponseEntity responseEntity = restTemplate.exchange(URL, HttpMethod.GET, entity, Map.class); - int status = responseEntity.getStatusCodeValue(); + HttpStatusCode status = responseEntity.getStatusCode(); - log.info("search maven central response code: " + status); + log.info("search maven central response code: " + status.value()); - if (status == 200) { + if (status.is2xxSuccessful()) { Map json = responseEntity.getBody(); Map response = (Map) json.get("response"); if (response != null) { @@ -71,11 +73,11 @@ public class BootVersionsFromMavenCentral { return versions; } else { - throw new Exception("unable to access Spring Boot versions from Maven Central, empty response"); + throw new IOException("Unable to access Spring Boot versions from Maven Central, empty response"); } } else { - throw new Exception("unable to access Spring Boot versions from Maven Central, query returned " + status); + throw new IOException("Failed to fetch versions from Maven Central, status = " + status.value()); } } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/validation/generations/CachedBootVersionsFromMavenCentral.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/validation/generations/CachedBootVersionsFromMavenCentral.java index 54f1abb95..9f13e4a08 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/validation/generations/CachedBootVersionsFromMavenCentral.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/validation/generations/CachedBootVersionsFromMavenCentral.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2022 VMware, Inc. + * Copyright (c) 2022, 2023 VMware, Inc. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -10,8 +10,14 @@ *******************************************************************************/ package org.springframework.ide.vscode.boot.validation.generations; +import java.io.IOException; import java.time.Duration; import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -28,31 +34,45 @@ public class CachedBootVersionsFromMavenCentral { private static final String KEY = "cacheKey"; private static final Duration EXPIRES_AFTER = Duration.ofMinutes(60); + private static final int ATTEMPTS_NUMBER = 5; + private static final long RESPONSE_WAIT_TIME_MS = 1000; private static final LoadingCache> cache = CacheBuilder.newBuilder() .expireAfterWrite(EXPIRES_AFTER) .build(new CacheLoader>() { @Override - public List load(String key) { - try { - return BootVersionsFromMavenCentral.getBootVersions(); - } - catch (Exception e) { - return ImmutableList.of(); + public List load(String key) throws Exception { + for (int i = 0; i < ATTEMPTS_NUMBER; i++) { + try { + return getFuture().get(RESPONSE_WAIT_TIME_MS, TimeUnit.MILLISECONDS); + } catch (ExecutionException | TimeoutException e) { + // ignore exception - ask maven central again + } } + throw new Exception("Failed to fetch versions from Maven Central after " + ATTEMPTS_NUMBER + " tries."); } }); - public static List getBootVersions() { + public static synchronized List getBootVersions() { try { return cache.get(KEY); } - catch (Exception e) { - log.error("failed to load Spring Boot release information from maven central", e); + catch (ExecutionException e) { + log.error("Failed to load Spring Boot release information from maven central", e); return ImmutableList.of(); } } + + private static CompletableFuture> getFuture() { + return CompletableFuture.supplyAsync(() -> { + try { + return BootVersionsFromMavenCentral.getBootVersions(); + } catch (IOException e) { + throw new CompletionException(e); + } + }); + } }