Add remote host name to ssh tunnel config, so that it can
be used to compute correct request mapping links for live
hovers.
This commit is contained in:
Kris De Volder
2018-08-07 10:04:21 -07:00
parent 64e7bbad45
commit 243586a8f8
8 changed files with 175 additions and 32 deletions

View File

@@ -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

View File

@@ -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<String> remoteBootApps = new LiveSetVariable<>();
private static LiveSetVariable<Pair<String,String>> remoteBootApps = new LiveSetVariable<>();
public static LiveSetVariable<String> getRemoteBootApps() {
public static LiveSetVariable<Pair<String,String>> getRemoteBootApps() {
return remoteBootApps;
}

View File

@@ -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<ImmutableSet<String>> remoteAppsListener = (e, v) -> sendConfiguration();
private final ValueListener<ImmutableSet<Pair<String,String>>> 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<String, Object> settings = new HashMap<>();
Map<String, Object> 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<Pair<String, String>> 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);

View File

@@ -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);
}
}

View File

@@ -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> T getAs(Class<T> 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<String> getStringSet(String... names) {
ImmutableSet.Builder<String> strings = ImmutableSet.builder();
try {

View File

@@ -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<String,SpringBootApp> remoteAppByUrl = new HashMap<>();
private Map<RemoteBootAppData,SpringBootApp> 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<SpringBootApp> 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<String> urls = settings.getStringSet("boot-java", "remote-apps");
{ //Remove obsolete apps...
Iterator<Entry<String, SpringBootApp>> entries = remoteAppByUrl.entrySet().iterator();
while (entries.hasNext()) {
Entry<String, SpringBootApp> 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<RemoteBootAppData> newAppData = new HashSet<>(Arrays.asList(appData));
{ //Remove obsolete apps...
Iterator<Entry<RemoteBootAppData, SpringBootApp>> entries = remoteAppInstances.entrySet().iterator();
while (entries.hasNext()) {
Entry<RemoteBootAppData, SpringBootApp> 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());
});
}
}
}
}

View File

@@ -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;

View File

@@ -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`"
},