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 0679fdfd1..52593c920 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 @@ -24,7 +24,8 @@ Require-Bundle: org.eclipse.jdt.launching;bundle-version="3.9.0", org.eclipse.ui.ide, org.eclipse.jdt.core, org.eclipse.ui.editors;bundle-version="3.11.100", - org.springsource.ide.eclipse.commons.livexp + org.springsource.ide.eclipse.commons.livexp, + org.apache.commons.lang3 Import-Package: com.google.gson;version="2.7.0", org.eclipse.jface.preference, org.osgi.framework diff --git a/eclipse-language-servers/org.springframework.tooling.boot.ls/src/org/springframework/tooling/boot/ls/BootLanguageServerPlugin.java b/eclipse-language-servers/org.springframework.tooling.boot.ls/src/org/springframework/tooling/boot/ls/BootLanguageServerPlugin.java index 8a32cc3a4..04126eb3e 100644 --- a/eclipse-language-servers/org.springframework.tooling.boot.ls/src/org/springframework/tooling/boot/ls/BootLanguageServerPlugin.java +++ b/eclipse-language-servers/org.springframework.tooling.boot.ls/src/org/springframework/tooling/boot/ls/BootLanguageServerPlugin.java @@ -14,6 +14,7 @@ import java.io.IOException; import java.util.ArrayList; import java.util.List; +import org.apache.commons.lang3.tuple.Pair; import org.eclipse.jface.bindings.Binding; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.keys.IBindingService; @@ -96,9 +97,9 @@ public class BootLanguageServerPlugin extends AbstractUIPlugin { } } - private static LiveSetVariable remoteBootApps = new LiveSetVariable<>(); + private static LiveSetVariable> remoteBootApps = new LiveSetVariable<>(); - public static LiveSetVariable getRemoteBootApps() { + public static LiveSetVariable> getRemoteBootApps() { return remoteBootApps; } 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 9f3cdb187..0bacd85f8 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 @@ -18,7 +18,9 @@ import java.nio.file.FileSystems; import java.util.Arrays; import java.util.HashMap; import java.util.Map; +import java.util.stream.Collectors; +import org.apache.commons.lang3.tuple.Pair; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.jface.util.IPropertyChangeListener; import org.eclipse.jface.util.PropertyChangeEvent; @@ -30,6 +32,7 @@ import org.eclipse.lsp4j.jsonrpc.messages.ResponseMessage; import org.eclipse.lsp4j.services.LanguageServer; import org.springsource.ide.eclipse.commons.livexp.core.ValueListener; +import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; /** @@ -50,7 +53,7 @@ public class DelegatingStreamConnectionProvider implements StreamConnectionProvi private LanguageServer languageServer; private final IPropertyChangeListener configListener = (e) -> sendConfiguration(); - private final ValueListener> remoteAppsListener = (e, v) -> sendConfiguration(); + private final ValueListener>> remoteAppsListener = (e, v) -> sendConfiguration(); public DelegatingStreamConnectionProvider() { String port = System.getProperty("boot-java-ls-port"); @@ -123,6 +126,28 @@ public class DelegatingStreamConnectionProvider implements StreamConnectionProvi } } + public class RemoteBootAppData { + private String jmxurl; + private String host; + public RemoteBootAppData(String jmxurl, String host) { + super(); + this.jmxurl = jmxurl; + this.host = host; + } + public String getJmxurl() { + return jmxurl; + } + public void setJmxurl(String jmxurl) { + this.jmxurl = jmxurl; + } + public String getHost() { + return host; + } + public void setHost(String host) { + this.host = host; + } + } + private void sendConfiguration() { Map settings = new HashMap<>(); Map bootJavaObj = new HashMap<>(); @@ -134,7 +159,12 @@ public class DelegatingStreamConnectionProvider implements StreamConnectionProvi bootJavaObj.put("boot-hints", bootHint); bootJavaObj.put("change-detection", bootChangeDetection); - bootJavaObj.put("remote-apps", BootLanguageServerPlugin.getRemoteBootApps().getValues()); + ImmutableSet> remoteApps = BootLanguageServerPlugin.getRemoteBootApps().getValues(); + bootJavaObj.put("remote-apps", BootLanguageServerPlugin.getRemoteBootApps().getValues() + .stream() + .map(pair -> new RemoteBootAppData(pair.getLeft(), pair.getRight())) + .collect(Collectors.toList()) + ); settings.put("boot-java", bootJavaObj); diff --git a/headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/RemoteSpringBootApp.java b/headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/RemoteSpringBootApp.java index c2a41cd9d..393402a8d 100644 --- a/headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/RemoteSpringBootApp.java +++ b/headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/RemoteSpringBootApp.java @@ -21,9 +21,16 @@ import org.springframework.ide.vscode.commons.util.MemoizingProxy; public class RemoteSpringBootApp extends AbstractSpringBootApp { private String jmxUrl; + private String host = null; + private String port = "80"; - protected RemoteSpringBootApp(String jmxUrl) { + protected RemoteSpringBootApp(String jmxUrl, String host) { this.jmxUrl = jmxUrl; + this.host = host; + } + + public void setHost(String host) { + this.host = host; } @Override @@ -31,6 +38,19 @@ public class RemoteSpringBootApp extends AbstractSpringBootApp { return jmxUrl; } + @Override + public String getPort() throws Exception { + return port!=null ? port : super.getPort(); + } + + @Override + public String getHost() throws Exception { + if (host!=null) { + return host; + } + return super.getHost(); + } + @Override public Properties getSystemProperties() throws Exception { return withPlatformMxBean(RuntimeMXBean.class, runtime -> { @@ -78,8 +98,8 @@ public class RemoteSpringBootApp extends AbstractSpringBootApp { return "Unknown"; } - public static SpringBootApp create(String jmxUrl) { - return MemoizingProxy.create(RemoteSpringBootApp.class, Duration.ofMillis(4900), jmxUrl); + public static SpringBootApp create(String jmxUrl, String host) { + return MemoizingProxy.create(RemoteSpringBootApp.class, Duration.ofMillis(4900), jmxUrl, host); } } diff --git a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/Settings.java b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/Settings.java index 911493839..9ca33e5cc 100644 --- a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/Settings.java +++ b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/Settings.java @@ -16,6 +16,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.collect.ImmutableSet; +import com.google.gson.Gson; import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; @@ -30,10 +31,27 @@ public class Settings { private JsonElement settings; + private Gson gson; + public Settings(JsonElement settings) { this.settings = settings; } + public T getAs(Class type, String... names) { + JsonElement json = getRawProperty(names); + if (json!=null) { + return gson().fromJson(json, type); + } + return null; + } + + private Gson gson() { + if (gson==null) { + gson = new Gson(); + } + return gson; + } + public Set getStringSet(String... names) { ImmutableSet.Builder strings = ImmutableSet.builder(); try { diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/RemoteRunningAppsProvider.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/RemoteRunningAppsProvider.java index 00cdd81dd..60ae625b4 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/RemoteRunningAppsProvider.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/RemoteRunningAppsProvider.java @@ -10,8 +10,10 @@ *******************************************************************************/ package org.springframework.ide.vscode.boot.java.handlers; +import java.util.Arrays; import java.util.Collection; import java.util.HashMap; +import java.util.HashSet; import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; @@ -19,6 +21,7 @@ import java.util.Set; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.ide.vscode.boot.java.handlers.RemoteRunningAppsProvider.RemoteBootAppData; import org.springframework.ide.vscode.commons.boot.app.cli.RemoteSpringBootApp; import org.springframework.ide.vscode.commons.boot.app.cli.SpringBootApp; import org.springframework.ide.vscode.commons.languageserver.util.Settings; @@ -27,13 +30,66 @@ import org.springframework.ide.vscode.commons.util.CollectorUtil; public class RemoteRunningAppsProvider implements RunningAppProvider { + public static class RemoteBootAppData { + private String jmxurl; + private String host; + + public String getJmxurl() { + return jmxurl; + } + public void setJmxurl(String jmxurl) { + this.jmxurl = jmxurl; + } + public String getHost() { + return host; + } + public void setHost(String host) { + this.host = host; + } + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((host == null) ? 0 : host.hashCode()); + result = prime * result + ((jmxurl == null) ? 0 : jmxurl.hashCode()); + return result; + } + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + RemoteBootAppData other = (RemoteBootAppData) obj; + if (host == null) { + if (other.host != null) + return false; + } else if (!host.equals(other.host)) + return false; + if (jmxurl == null) { + if (other.jmxurl != null) + return false; + } else if (!jmxurl.equals(other.jmxurl)) + return false; + return true; + } + @Override + public String toString() { + return "RemoteBootAppData [jmxurl=" + jmxurl + ", host=" + host + "]"; + } + + + } + private static Logger logger = LoggerFactory.getLogger(RemoteRunningAppsProvider.class); /** - * We keep the remote app instances in a Map indexed by url. This allows us to - * return the same instance(s) repeatedly as long as the urls do not change. + * We keep the remote app instances in a Map indexed by the json daya. This allows us to + * return the same instance(s) repeatedly as long as the data does not change. */ - private Map remoteAppByUrl = new HashMap<>(); + private Map remoteAppInstances = new HashMap<>(); public RemoteRunningAppsProvider(SimpleLanguageServer server) { server.getWorkspaceService().onDidChangeConfiguraton(this::handleSettings); @@ -41,31 +97,36 @@ public class RemoteRunningAppsProvider implements RunningAppProvider { @Override public synchronized Collection getAllRunningSpringApps() throws Exception { - return remoteAppByUrl.values().stream().filter(SpringBootApp::isSpringBootApp).collect(CollectorUtil.toImmutableList()); + return remoteAppInstances.values().stream().filter(SpringBootApp::isSpringBootApp).collect(CollectorUtil.toImmutableList()); } synchronized void handleSettings(Settings settings) { - Set urls = settings.getStringSet("boot-java", "remote-apps"); - - { //Remove obsolete apps... - Iterator> entries = remoteAppByUrl.entrySet().iterator(); - while (entries.hasNext()) { - Entry entry = entries.next(); - String key = entry.getKey(); - if (!urls.contains(key)) { - logger.debug("Removing RemoteSpringBootApp: "+key); - entries.remove(); - entry.getValue().dispose(); + RemoteBootAppData[] appData = settings.getAs(RemoteBootAppData[].class, "boot-java", "remote-apps"); + if (appData==null || appData.length==0) { + logger.info("Clearing all RemoteSpringBootApps"); + remoteAppInstances.clear(); + } else { + Set newAppData = new HashSet<>(Arrays.asList(appData)); + { //Remove obsolete apps... + Iterator> entries = remoteAppInstances.entrySet().iterator(); + while (entries.hasNext()) { + Entry entry = entries.next(); + RemoteBootAppData key = entry.getKey(); + if (!newAppData.contains(key)) { + logger.info("Removing RemoteSpringBootApp: "+key); + entries.remove(); + entry.getValue().dispose(); + } } } - } - { //Add new apps - for (String url : urls) { - remoteAppByUrl.computeIfAbsent(url, (_url) -> { - logger.debug("Creating RemoteStringBootApp: "+_url); - return RemoteSpringBootApp.create(_url); - }); + { //Add new apps + for (RemoteBootAppData key : newAppData) { + remoteAppInstances.computeIfAbsent(key, (_key) -> { + logger.info("Creating RemoteStringBootApp: "+_key); + return RemoteSpringBootApp.create(key.getJmxurl(), key.getHost()); + }); + } } } } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/UrlUtil.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/UrlUtil.java index f961f10f7..25e9e739d 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/UrlUtil.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/UrlUtil.java @@ -28,7 +28,11 @@ public class UrlUtil { if (!path.startsWith("/")) { path = "/" +path; } - return "http://"+host+":"+port+path; + if (port.equals("80")) { + return "http://"+host+path; + } else { + return "http://"+host+":"+port+path; + } } } return null; diff --git a/vscode-extensions/vscode-spring-boot/package.json b/vscode-extensions/vscode-spring-boot/package.json index 5db346337..30e4a6f8c 100644 --- a/vscode-extensions/vscode-spring-boot/package.json +++ b/vscode-extensions/vscode-spring-boot/package.json @@ -76,7 +76,15 @@ "boot-java.remote-apps": { "type": "array", "items": { - "type": "string" + "type": "object", + "properties": { + "jmxurl": { + "type": "string" + }, + "host": { + "type": "string" + } + } }, "description": "Array of jmx urls pointing to remote spring boot applications to poll for live hover information. A typical url looks something like this: `service:jmx:rmi://localhost:9111/jndi/rmi://localhost:9111/jmxrmi`" },