From d7ae292fd8f413ae0228d2355118282a8729655b Mon Sep 17 00:00:00 2001 From: aboyko Date: Wed, 6 Sep 2023 19:27:01 -0400 Subject: [PATCH] Http Proxy server support --- .../META-INF/MANIFEST.MF | 3 +- .../DelegatingStreamConnectionProvider.java | 57 ++++++++++++-- .../spring-boot-language-server/pom.xml | 4 + .../boot/app/BootVersionValidationConfig.java | 18 ++++- .../vscode/boot/app/RestTemplateFactory.java | 77 +++++++++++++++++++ .../BootVersionsFromMavenCentral.java | 13 +++- .../CachedBootVersionsFromMavenCentral.java | 14 +++- .../generations/GenerationsValidator.java | 5 +- .../generations/SpringIoProjectsProvider.java | 11 ++- .../generations/SpringProjectsClient.java | 11 ++- .../generations/UpdateBootVersion.java | 7 +- .../json/ResolvedSpringProject.java | 5 +- .../ProjectGenerationsValidationTest.java | 6 +- .../test/SampleProjectsProvider.java | 2 +- .../vscode-spring-boot/lib/Main.ts | 2 +- 15 files changed, 203 insertions(+), 32 deletions(-) create mode 100644 headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/RestTemplateFactory.java diff --git a/eclipse-language-servers/org.springframework.tooling.boot.ls/META-INF/MANIFEST.MF b/eclipse-language-servers/org.springframework.tooling.boot.ls/META-INF/MANIFEST.MF index dd6cd1760..e9eb802d9 100644 --- a/eclipse-language-servers/org.springframework.tooling.boot.ls/META-INF/MANIFEST.MF +++ b/eclipse-language-servers/org.springframework.tooling.boot.ls/META-INF/MANIFEST.MF @@ -36,7 +36,8 @@ Require-Bundle: org.eclipse.jdt.launching;bundle-version="3.9.0", org.eclipse.core.expressions, org.springsource.ide.eclipse.commons.core;bundle-version="4.17.0", org.springframework.tooling.jdt.ls.commons;bundle-version="4.17.0", - org.eclipse.jface.notifications + org.eclipse.jface.notifications, + org.eclipse.core.net Import-Package: com.google.common.base, com.google.common.collect, com.google.gson, diff --git a/eclipse-language-servers/org.springframework.tooling.boot.ls/src/org/springframework/tooling/boot/ls/DelegatingStreamConnectionProvider.java b/eclipse-language-servers/org.springframework.tooling.boot.ls/src/org/springframework/tooling/boot/ls/DelegatingStreamConnectionProvider.java index ac2e4b50d..a36cc7058 100644 --- a/eclipse-language-servers/org.springframework.tooling.boot.ls/src/org/springframework/tooling/boot/ls/DelegatingStreamConnectionProvider.java +++ b/eclipse-language-servers/org.springframework.tooling.boot.ls/src/org/springframework/tooling/boot/ls/DelegatingStreamConnectionProvider.java @@ -21,6 +21,10 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import org.eclipse.core.internal.net.ProxyManager; +import org.eclipse.core.net.proxy.IProxyChangeListener; +import org.eclipse.core.net.proxy.IProxyData; +import org.eclipse.core.net.proxy.IProxyService; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.Assert; import org.eclipse.core.runtime.preferences.IEclipsePreferences; @@ -32,6 +36,7 @@ import org.eclipse.lsp4j.InitializeResult; import org.eclipse.lsp4j.jsonrpc.messages.Message; import org.eclipse.lsp4j.jsonrpc.messages.ResponseMessage; import org.eclipse.lsp4j.services.LanguageServer; +import org.eclipse.ui.PlatformUI; import org.springframework.tooling.boot.ls.prefs.CategoryProblemsSeverityPrefsPage; import org.springframework.tooling.boot.ls.prefs.FileListEditor; import org.springframework.tooling.boot.ls.prefs.ProblemCategoryData; @@ -55,6 +60,7 @@ import com.google.common.collect.ImmutableSet; * * @author Martin Lippert */ +@SuppressWarnings("restriction") public class DelegatingStreamConnectionProvider implements StreamConnectionProvider { private StreamConnectionProvider provider; @@ -70,8 +76,7 @@ public class DelegatingStreamConnectionProvider implements StreamConnectionProvi private final ValueListener> remoteAppsListener = (e, v) -> sendConfiguration(); - private long timestampBeforeStart; - private long timestampWhenInitialized; + private final IProxyChangeListener proxySettingsListener = e -> sendConfiguration(); public DelegatingStreamConnectionProvider() { // LanguageServerCommonsActivator.logInfo("Entering DelegatingStreamConnectionProvider()"); @@ -94,9 +99,13 @@ public class DelegatingStreamConnectionProvider implements StreamConnectionProvi @Override public void start() throws IOException { - this.timestampBeforeStart = System.currentTimeMillis(); BootLanguageServerPlugin.getDefault().getLog().info("DelegatingStreamConnectionProvider - Starting Boot LS"); this.provider.start(); + IProxyService proxyService = PlatformUI.getWorkbench().getService(IProxyService.class); + if (proxyService != null) { + proxyService.addProxyChangeListener(proxySettingsListener); + } + } @Override @@ -116,6 +125,10 @@ public class DelegatingStreamConnectionProvider implements StreamConnectionProvi @Override public void stop() { + IProxyService proxyService = PlatformUI.getWorkbench().getService(IProxyService.class); + if (proxyService != null) { + proxyService.removeProxyChangeListener(proxySettingsListener); + } BootLanguageServerPlugin.getDefault().getLog().info("DelegatingStreamConnectionProvider - Stopping Boot LS"); this.provider.stop(); if (fResourceListener != null) { @@ -134,8 +147,6 @@ public class DelegatingStreamConnectionProvider implements StreamConnectionProvi if (responseMessage.getResult() instanceof InitializeResult) { this.languageServer = languageServer; - this.timestampWhenInitialized = System.currentTimeMillis(); -// LanguageServerCommonsActivator.logInfo("Boot LS startup time from start to initialized: " + (timestampWhenInitialized - timestampBeforeStart) + "ms"); sendConfiguration(); @@ -214,12 +225,47 @@ public class DelegatingStreamConnectionProvider implements StreamConnectionProvi settings.put("boot-java", bootJavaObj); + settings.put("http", createHttpProxySettings()); + putValidationPreferences(settings); putValidationCategoryToggles(settings); this.languageServer.getWorkspaceService().didChangeConfiguration(new DidChangeConfigurationParams(settings)); } + private Map createHttpProxySettings() { + Map proxy = new HashMap<>(); + IProxyService proxyService = PlatformUI.getWorkbench().getService(IProxyService.class); + if ((proxyService.isProxiesEnabled() || proxyService.isSystemProxiesEnabled()) && proxyService instanceof ProxyManager) { + ProxyManager proxyManager = (ProxyManager) proxyService; + if (proxyService.isSystemProxiesEnabled() && proxyManager.hasSystemProxies()) { + for (IProxyData data : proxyManager.getNativeProxyData()) { + if (data.getHost() != null) { + fillProxyData(proxy, data, proxyManager.getNativeNonProxiedHosts()); + break; + } + } + } else if (proxyService.isProxiesEnabled()) { + for (IProxyData data : proxyService.getProxyData()) { + if (data.getHost() != null) { + fillProxyData(proxy, data, proxyService.getNonProxiedHosts()); + break; + } + } + } + } + return proxy; + } + + private static void fillProxyData(Map proxy, IProxyData data, String[] exclusions) { + proxy.put("proxy", data.getType().toLowerCase() + "://" + data.getHost() + (data.getPort() >= 0 ? ":" + data.getPort() : "")); + if (data.isRequiresAuthentication()) { + proxy.put("proxy-user", data.getUserId()); + proxy.put("proxy-password", data.getPassword()); + } + proxy.put("proxy-exclusions", exclusions); + } + private void putValidationPreferences(Map settings) { try { IEclipsePreferences prefs = BootLanguageServerPlugin.getPreferences(); @@ -253,6 +299,7 @@ public class DelegatingStreamConnectionProvider implements StreamConnectionProvi } } + @SuppressWarnings("unchecked") private void dotPut(Object _settings, String dottedProperty, Object value) { if (_settings instanceof Map) { Map settings = (Map) _settings; diff --git a/headless-services/spring-boot-language-server/pom.xml b/headless-services/spring-boot-language-server/pom.xml index 67631c9f8..9cc823d61 100644 --- a/headless-services/spring-boot-language-server/pom.xml +++ b/headless-services/spring-boot-language-server/pom.xml @@ -158,6 +158,10 @@ + + com.squareup.okhttp3 + okhttp + 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 cc9d13872..f1a9c0128 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 @@ -22,6 +22,8 @@ import org.springframework.context.annotation.Configuration; import org.springframework.ide.vscode.boot.common.ProjectReconcileScheduler; import org.springframework.ide.vscode.boot.java.rewrite.SpringBootUpgrade; import org.springframework.ide.vscode.boot.validation.BootVersionValidationEngine; +import org.springframework.ide.vscode.boot.validation.generations.BootVersionsFromMavenCentral; +import org.springframework.ide.vscode.boot.validation.generations.CachedBootVersionsFromMavenCentral; import org.springframework.ide.vscode.boot.validation.generations.GenerationsValidator; import org.springframework.ide.vscode.boot.validation.generations.ProjectVersionDiagnosticProvider; import org.springframework.ide.vscode.boot.validation.generations.UpdateBootVersion; @@ -36,18 +38,26 @@ public class BootVersionValidationConfig { private static final Logger log = LoggerFactory.getLogger(BootVersionValidationConfig.class); - @Bean UpdateBootVersion updateBootVersion(SimpleLanguageServer server, Optional bootUpgradeOpt) { - return new UpdateBootVersion(server.getDiagnosticSeverityProvider(), bootUpgradeOpt); + @Bean UpdateBootVersion updateBootVersion(SimpleLanguageServer server, Optional bootUpgradeOpt, CachedBootVersionsFromMavenCentral cachedVersionsFromMaven) { + return new UpdateBootVersion(server.getDiagnosticSeverityProvider(), bootUpgradeOpt, cachedVersionsFromMaven); } - @Bean GenerationsValidator generationsValidator(SimpleLanguageServer server, BootJavaConfig config) { - return new GenerationsValidator(server.getDiagnosticSeverityProvider(), config); + @Bean GenerationsValidator generationsValidator(SimpleLanguageServer server, BootJavaConfig config, RestTemplateFactory restTemplateFactory, CachedBootVersionsFromMavenCentral cachedVersionsFromMaven) { + return new GenerationsValidator(server.getDiagnosticSeverityProvider(), config, restTemplateFactory, cachedVersionsFromMaven); } @Bean ProjectVersionDiagnosticProvider projectVersionDiagnosticProvider(List validators) { return new ProjectVersionDiagnosticProvider(validators); } + @Bean BootVersionsFromMavenCentral booVersionsFromMavenCentral(RestTemplateFactory restTemplateFactory) { + return new BootVersionsFromMavenCentral(restTemplateFactory); + } + + @Bean CachedBootVersionsFromMavenCentral cachedBootVersionsFromMavenCentral(BootVersionsFromMavenCentral bootVersionsFromMaven) { + return new CachedBootVersionsFromMavenCentral(bootVersionsFromMaven); + } + @ConditionalOnMissingClass("org.springframework.ide.vscode.languageserver.testharness.LanguageServerHarness") @ConditionalOnProperty(prefix = "languageserver", name = "reconcile-only-opened-docs", havingValue = "false", matchIfMissing = true) @Bean diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/RestTemplateFactory.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/RestTemplateFactory.java new file mode 100644 index 000000000..2dbbf3736 --- /dev/null +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/RestTemplateFactory.java @@ -0,0 +1,77 @@ +/******************************************************************************* + * 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.app; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.net.MalformedURLException; +import java.net.Proxy; +import java.net.URL; +import java.util.Set; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.client.OkHttp3ClientHttpRequestFactory; +import org.springframework.stereotype.Component; +import org.springframework.web.client.RestTemplate; + +import okhttp3.Authenticator; +import okhttp3.Credentials; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; +import okhttp3.Route; + +@Component +public class RestTemplateFactory { + + private static final Logger log = LoggerFactory.getLogger(RestTemplateFactory.class); + + private BootJavaConfig config; + + public RestTemplateFactory(BootJavaConfig config) { + this.config = config; + } + + public RestTemplate createRestTemplate(String host) { + String proxyUrl = config.getRawSettings().getString("http", "proxy"); + OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder(); + if (proxyUrl != null && !proxyUrl.isBlank()) { + Set exclusions = config.getRawSettings().getStringSet("http", "proxy-exclusions"); + if (!"localhost".equals(host) && !"127.0.0.1".equals(host) && !exclusions.contains(host)) { + try { + URL url = new URL(proxyUrl); + if (url.getProtocol().startsWith("http")) { + clientBuilder.proxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress(url.getHost(), url.getPort()))); + } else if (url.getProtocol().startsWith("sock")) { + clientBuilder.proxy(new Proxy(Proxy.Type.SOCKS, new InetSocketAddress(url.getHost(), url.getPort()))); + } + String username = config.getRawSettings().getString("http", "proxy-user"); + String password = config.getRawSettings().getString("http", "proxy-password"); + if (username != null && password != null && !username.isEmpty()) { + clientBuilder.proxyAuthenticator(new Authenticator() { + @Override + public Request authenticate(Route route, Response response) throws IOException { + String credential = Credentials.basic(username, password); + return response.request().newBuilder().header("Proxy-Authorization", credential) + .build(); + } + }); + } + } catch (MalformedURLException e) { + log.error("", e); + } + } + } + return new RestTemplate(new OkHttp3ClientHttpRequestFactory(clientBuilder.build())); + } + +} 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 9d25ded83..ee61cfe45 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 @@ -11,6 +11,7 @@ package org.springframework.ide.vscode.boot.validation.generations; import java.io.IOException; +import java.net.URI; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -24,6 +25,7 @@ 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.boot.app.RestTemplateFactory; import org.springframework.ide.vscode.commons.java.SpringProjectUtil; import org.springframework.ide.vscode.commons.java.Version; import org.springframework.web.client.RestTemplate; @@ -31,15 +33,20 @@ import org.springframework.web.client.RestTemplate; public class BootVersionsFromMavenCentral { private static final Logger log = LoggerFactory.getLogger(BootVersionsFromMavenCentral.class); - 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"; + private static final URI URL = URI.create("https://search.maven.org/solrsearch/select?q=g:org.springframework.boot+AND+a:spring-boot-starter-parent&core=gav&rows=200&wt=json"); + private RestTemplateFactory restTemplateFactory; + public BootVersionsFromMavenCentral(RestTemplateFactory restTemplateFactory) { + this.restTemplateFactory = restTemplateFactory; + } + @SuppressWarnings({ "rawtypes", "unchecked" }) - public static List getBootVersions() throws IOException { + public List getBootVersions() throws IOException { HttpHeaders headers = new HttpHeaders(); headers.setAccept(MediaType.parseMediaTypes("application/json")); HttpEntity entity = new HttpEntity<>(headers); - RestTemplate restTemplate = new RestTemplate(); + RestTemplate restTemplate = restTemplateFactory.createRestTemplate(URL.getHost()); log.info("search maven central for Spring Boot release information via: " + URL); 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 56b623cea..50e32a0c0 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 @@ -37,8 +37,14 @@ public class CachedBootVersionsFromMavenCentral { 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 BootVersionsFromMavenCentral bootVersionsFromMaven; - private static final LoadingCache> cache = CacheBuilder.newBuilder() + public CachedBootVersionsFromMavenCentral(BootVersionsFromMavenCentral bootVersionsFromMaven) { + this.bootVersionsFromMaven = bootVersionsFromMaven; + } + + private final LoadingCache> cache = CacheBuilder.newBuilder() .expireAfterWrite(EXPIRES_AFTER) .build(new CacheLoader>() { @@ -62,7 +68,7 @@ public class CachedBootVersionsFromMavenCentral { }); - public static synchronized List getBootVersions() { + public synchronized List getBootVersions() { try { return cache.get(KEY); } @@ -72,10 +78,10 @@ public class CachedBootVersionsFromMavenCentral { } } - private static CompletableFuture> getFuture() { + private CompletableFuture> getFuture() { return CompletableFuture.supplyAsync(() -> { try { - return BootVersionsFromMavenCentral.getBootVersions(); + return bootVersionsFromMaven.getBootVersions(); } catch (IOException e) { throw new CompletionException(e); } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/validation/generations/GenerationsValidator.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/validation/generations/GenerationsValidator.java index 9fde1286d..c78f8dbeb 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/validation/generations/GenerationsValidator.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/validation/generations/GenerationsValidator.java @@ -15,6 +15,7 @@ import java.util.List; import org.eclipse.lsp4j.Diagnostic; 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.Generation; import org.springframework.ide.vscode.boot.validation.generations.json.ResolvedSpringProject; import org.springframework.ide.vscode.boot.validation.generations.preferences.VersionValidationProblemType; @@ -30,9 +31,9 @@ public class GenerationsValidator extends AbstractDiagnosticValidator { private SpringIoProjectsProvider provider; - public GenerationsValidator(DiagnosticSeverityProvider diagnosticSeverityProvider, BootJavaConfig config) { + public GenerationsValidator(DiagnosticSeverityProvider diagnosticSeverityProvider, BootJavaConfig config, RestTemplateFactory restTemplateFactory, CachedBootVersionsFromMavenCentral cachedVersionsFromMaven) { super(diagnosticSeverityProvider); - provider = new SpringIoProjectsProvider(config.getSpringIOApiUrl()); + provider = new SpringIoProjectsProvider(config.getSpringIOApiUrl(), restTemplateFactory, cachedVersionsFromMaven); config.addListener(v -> provider.updateIoApiUri(config.getSpringIOApiUrl())); } 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 3b2e7ed61..2d0e5c741 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 @@ -13,6 +13,7 @@ package org.springframework.ide.vscode.boot.validation.generations; import java.util.List; import java.util.Map; +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; @@ -32,14 +33,18 @@ public class SpringIoProjectsProvider implements SpringProjectsProvider { private SpringProjectsClient client; private Map cache; + private RestTemplateFactory restTemplateFactory; + private CachedBootVersionsFromMavenCentral cachedVersionsFromMaven; - public SpringIoProjectsProvider(String uri) { + public SpringIoProjectsProvider(String uri, RestTemplateFactory restTemplateFactory, CachedBootVersionsFromMavenCentral cachedVersionsFromMaven) { + this.restTemplateFactory = restTemplateFactory; updateIoApiUri(uri); } public synchronized void updateIoApiUri(String uri) { if (client == null || !uri.equals(client.getUrl())) { - this.client = new SpringProjectsClient(uri); + this.client = new SpringProjectsClient(uri, restTemplateFactory); + cache = null; } } @@ -70,7 +75,7 @@ public class SpringIoProjectsProvider implements SpringProjectsProvider { List projects = springProjects.getProjects(); if (projects != null) { for (SpringProject project : projects) { - builder.put(project.getSlug(), new ResolvedSpringProject(project, client)); + builder.put(project.getSlug(), new ResolvedSpringProject(project, client, cachedVersionsFromMaven)); } } } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/validation/generations/SpringProjectsClient.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/validation/generations/SpringProjectsClient.java index a3ba7f068..3e5a90a72 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/validation/generations/SpringProjectsClient.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/validation/generations/SpringProjectsClient.java @@ -10,6 +10,7 @@ *******************************************************************************/ package org.springframework.ide.vscode.boot.validation.generations; +import java.net.URI; import java.util.Map; import org.springframework.http.HttpEntity; @@ -17,6 +18,7 @@ import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; +import org.springframework.ide.vscode.boot.app.RestTemplateFactory; 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; @@ -27,9 +29,11 @@ import com.fasterxml.jackson.databind.ObjectMapper; public class SpringProjectsClient { private final String url; + private RestTemplateFactory restTemplateFactory; - public SpringProjectsClient(String url) { + public SpringProjectsClient(String url, RestTemplateFactory restTemplateFactory) { this.url = url; + this.restTemplateFactory = restTemplateFactory; } public String getUrl() { @@ -70,8 +74,9 @@ public class SpringProjectsClient { @SuppressWarnings({ "rawtypes", "unchecked" }) HttpEntity entity = new HttpEntity(headers); - RestTemplate restTemplate = new RestTemplate(); - ResponseEntity response = restTemplate.exchange(url, HttpMethod.GET, entity, clazz); + URI uri = URI.create(url); + RestTemplate restTemplate = restTemplateFactory.createRestTemplate(uri.getHost()); + ResponseEntity response = restTemplate.exchange(uri, HttpMethod.GET, entity, clazz); return response.getBody(); } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/validation/generations/UpdateBootVersion.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/validation/generations/UpdateBootVersion.java index ad75b706e..208ce7d63 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/validation/generations/UpdateBootVersion.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/validation/generations/UpdateBootVersion.java @@ -36,14 +36,17 @@ public class UpdateBootVersion extends AbstractDiagnosticValidator { private Optional bootUpgradeOpt; - public UpdateBootVersion(DiagnosticSeverityProvider diagnosticSeverityProvider, Optional bootUpgradeOpt) { + private CachedBootVersionsFromMavenCentral cachedVersionsFromMaven; + + public UpdateBootVersion(DiagnosticSeverityProvider diagnosticSeverityProvider, Optional bootUpgradeOpt, CachedBootVersionsFromMavenCentral cachedVersionsFromMaven) { super(diagnosticSeverityProvider); this.bootUpgradeOpt = bootUpgradeOpt; + this.cachedVersionsFromMaven = cachedVersionsFromMaven; } @Override public Collection validate(IJavaProject javaProject, Version javaProjectVersion) throws Exception { - List versions = CachedBootVersionsFromMavenCentral.getBootVersions(); + List versions = cachedVersionsFromMaven.getBootVersions(); ImmutableList.Builder builder = ImmutableList.builder(); validateMajorVersion(javaProject, javaProjectVersion, versions).ifPresent(builder::add); validateMinorVersion(javaProject, javaProjectVersion, versions).ifPresent(builder::add); diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/validation/generations/json/ResolvedSpringProject.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/validation/generations/json/ResolvedSpringProject.java index 682ea2c24..8fc31d2b0 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/validation/generations/json/ResolvedSpringProject.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/validation/generations/json/ResolvedSpringProject.java @@ -22,8 +22,9 @@ public class ResolvedSpringProject extends SpringProject { private final SpringProjectsClient client; private Generations generations; + private CachedBootVersionsFromMavenCentral cachedVersionsFromMaven; - public ResolvedSpringProject(SpringProject project, SpringProjectsClient client) { + public ResolvedSpringProject(SpringProject project, SpringProjectsClient client, CachedBootVersionsFromMavenCentral cachedVersionsFromMaven) { this.client = client; setName(project.getName()); setRepositoryUrl(project.getRepositoryUrl()); @@ -52,7 +53,7 @@ public class ResolvedSpringProject extends SpringProject { * @throws Exception */ public List getReleases() throws Exception { - return CachedBootVersionsFromMavenCentral.getBootVersions(); + return cachedVersionsFromMaven.getBootVersions(); } } 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 6c6999017..37353a9dd 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 @@ -22,8 +22,10 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Import; +import org.springframework.ide.vscode.boot.app.RestTemplateFactory; import org.springframework.ide.vscode.boot.bootiful.BootLanguageServerTest; import org.springframework.ide.vscode.boot.bootiful.HoverTestConf; +import org.springframework.ide.vscode.boot.validation.generations.CachedBootVersionsFromMavenCentral; import org.springframework.ide.vscode.boot.validation.generations.SpringIoProjectsProvider; import org.springframework.ide.vscode.boot.validation.generations.SpringProjectsProvider; import org.springframework.ide.vscode.boot.validation.generations.json.Generation; @@ -42,6 +44,8 @@ import org.springframework.test.context.junit.jupiter.SpringExtension; public class ProjectGenerationsValidationTest { @Autowired private BootLanguageServerHarness harness; + @Autowired private RestTemplateFactory restTemplateFactory; + @Autowired private CachedBootVersionsFromMavenCentral cachedVersions; private ProjectsHarness projects = ProjectsHarness.INSTANCE; @@ -55,7 +59,7 @@ public class ProjectGenerationsValidationTest { @Test void testProjectsInfoFromSpringIo() throws Exception { - SpringProjectsProvider cache = new SpringIoProjectsProvider("https://api.spring.io/projects"); + SpringProjectsProvider cache = new SpringIoProjectsProvider("https://api.spring.io/projects", restTemplateFactory, cachedVersions); SpringProject project = cache.getProject("spring-boot"); assertNotNull(project); diff --git a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/validation/test/SampleProjectsProvider.java b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/validation/test/SampleProjectsProvider.java index e3c733e82..e8858c5eb 100644 --- a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/validation/test/SampleProjectsProvider.java +++ b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/validation/test/SampleProjectsProvider.java @@ -34,7 +34,7 @@ public class SampleProjectsProvider implements SpringProjectsProvider { @Override public ResolvedSpringProject getProject(String projectSlug) throws Exception { - ResolvedSpringProject project = new ResolvedSpringProject(getSpringProject(projectSlug), null) { + ResolvedSpringProject project = new ResolvedSpringProject(getSpringProject(projectSlug), null, null) { @Override public List getGenerations() throws Exception { diff --git a/vscode-extensions/vscode-spring-boot/lib/Main.ts b/vscode-extensions/vscode-spring-boot/lib/Main.ts index 01302af7c..138605d60 100644 --- a/vscode-extensions/vscode-spring-boot/lib/Main.ts +++ b/vscode-extensions/vscode-spring-boot/lib/Main.ts @@ -118,7 +118,7 @@ export function activate(context: VSCode.ExtensionContext): Thenable ({ workspaceFolders: workspace.workspaceFolders ? workspace.workspaceFolders.map(f => f.uri.toString()) : null,