PT 163178235 - Add live properties parsing for Boot 2.x

Live properties parsing for Boot 2.x was missing and was not appearing
when hovering over @Value properties
This commit is contained in:
nsingh@pivotal.io
2019-02-11 13:35:20 -08:00
parent b9c3981529
commit cd276ddbc4
9 changed files with 410 additions and 27 deletions

View File

@@ -43,6 +43,8 @@ import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ide.vscode.commons.boot.app.cli.livebean.LiveBeansModel;
import org.springframework.ide.vscode.commons.boot.app.cli.liveproperties.LiveEnvJsonParser;
import org.springframework.ide.vscode.commons.boot.app.cli.liveproperties.LiveProperties;
import org.springframework.ide.vscode.commons.boot.app.cli.requestmappings.Boot1xRequestMapping;
import org.springframework.ide.vscode.commons.boot.app.cli.requestmappings.RequestMapping;
import org.springframework.ide.vscode.commons.boot.app.cli.requestmappings.RequestMappingsParser20;
@@ -623,6 +625,20 @@ public abstract class AbstractSpringBootApp implements SpringBootApp {
});
}
@Override
public LiveProperties getLiveProperties() throws Exception {
try {
String envJson = getEnvironment();
if (envJson != null) {
return LiveEnvJsonParser.parseProperties(envJson);
}
} catch (Exception e) {
logger.error("error resolving live properties from environment endpoint", e);
}
return null;
}
protected String getPortViaAdmin(MBeanServerConnection connection) throws Exception {
try {
String DEFAULT_OBJECT_NAME = "org.springframework.boot:type=Admin,name=SpringApplication";

View File

@@ -16,6 +16,7 @@ import java.util.Optional;
import java.util.Properties;
import org.springframework.ide.vscode.commons.boot.app.cli.livebean.LiveBeansModel;
import org.springframework.ide.vscode.commons.boot.app.cli.liveproperties.LiveProperties;
import org.springframework.ide.vscode.commons.boot.app.cli.requestmappings.RequestMapping;
import reactor.core.Disposable;
@@ -41,6 +42,8 @@ public interface SpringBootApp extends Disposable {
Optional<List<LiveConditional>> getLiveConditionals() throws Exception;
Properties getSystemProperties() throws Exception;
LiveProperties getLiveProperties() throws Exception;
default String getSystemProperty(String string) throws Exception {
Object r = getSystemProperties().get(string);
if (r instanceof String) {

View File

@@ -0,0 +1,56 @@
/*******************************************************************************
* Copyright (c) 2019 Pivotal, 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
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.commons.boot.app.cli.liveproperties;
import java.util.List;
import org.json.JSONException;
import org.json.JSONObject;
public abstract class LiveEnvJsonParser {
/**
* Parse live properties from the given JSON input. Returns null if no live
* properties could be parsed
*
* @param jsonInput
* @return live properties if parsed, or null otherwise
* @throws Exception
*/
public LiveProperties parse(String jsonInput) throws Exception {
JSONObject envObj = toJson(jsonInput);
List<LivePropertySource> propertySources = readProperties(envObj);
if (propertySources != null && !propertySources.isEmpty()) {
return new LiveProperties(propertySources);
} else {
return null;
}
}
protected JSONObject toJson(String json) throws JSONException {
return new JSONObject(json);
}
protected abstract List<LivePropertySource> readProperties(JSONObject envObj) throws Exception;
public static LiveProperties parseProperties(String jsonInput) throws Exception {
LiveEnvJsonParser2x boot2x = new LiveEnvJsonParser2x();
LiveProperties properties = boot2x.parse(jsonInput);
if (properties == null) {
LiveEnvJsonParser1x boot1x = new LiveEnvJsonParser1x();
properties = boot1x.parse(jsonInput);
}
return properties;
}
}

View File

@@ -0,0 +1,82 @@
/*******************************************************************************
* Copyright (c) 2019 Pivotal, 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
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.commons.boot.app.cli.liveproperties;
import java.util.Iterator;
import java.util.List;
import org.json.JSONObject;
import com.google.common.collect.ImmutableList;
public class LiveEnvJsonParser1x extends LiveEnvJsonParser {
public LiveEnvJsonParser1x() {
}
/**
*
* @param envObj
* @return non-null PropertySources. Content in the PropertySources may be empty
* if no sources are found
* @throws Exception
*/
@Override
protected List<LivePropertySource> readProperties(JSONObject allSourcesJson) throws Exception {
ImmutableList.Builder<LivePropertySource> allSources = ImmutableList.builder();
if (allSourcesJson != null) {
Iterator<?> keys = allSourcesJson.keys();
if (keys != null) {
while (keys.hasNext()) {
Object key = keys.next();
if (key instanceof String) {
String sourceName = (String) key;
// Skip profiles
if (!"profiles".equals(sourceName)) {
Object sourceObj = allSourcesJson.opt(sourceName);
ImmutableList.Builder<LiveProperty> parsedProps = ImmutableList.builder();
if (sourceObj instanceof JSONObject) {
JSONObject source = (JSONObject) sourceObj;
Iterator<?> propKeys = source.keys();
if (propKeys != null) {
while (propKeys.hasNext()) {
Object propObjKey = propKeys.next();
if (propObjKey instanceof String) {
String propName = (String) propObjKey;
Object valObj = source.optString(propName);
if (valObj instanceof String) {
String value = (String) valObj;
LiveProperty property = LiveProperty
.builder() //
.source(sourceName) //
.property(propName) //
.value(value) //
.build();
parsedProps.add(property);
}
}
}
}
}
LivePropertySource propertySource = new LivePropertySource(sourceName, parsedProps.build());
allSources.add(propertySource);
}
}
}
}
}
return allSources.build();
}
}

View File

@@ -0,0 +1,97 @@
/*******************************************************************************
* Copyright (c) 2019 Pivotal, 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
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.commons.boot.app.cli.liveproperties;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.json.JSONArray;
import org.json.JSONObject;
import com.google.common.collect.ImmutableList;
public class LiveEnvJsonParser2x extends LiveEnvJsonParser {
public LiveEnvJsonParser2x() {
}
/**
*
* @param envObj
* @return non-null PropertySources. Content in the PropertySources may be empty
* if no sources are found
* @throws Exception
*/
@Override
protected List<LivePropertySource> readProperties(JSONObject envObj) throws Exception {
ImmutableList.Builder<LivePropertySource> allSources = ImmutableList.builder();
Object sourcesObj = envObj.opt("propertySources");
if (sourcesObj instanceof JSONArray) {
JSONArray props = (JSONArray) sourcesObj;
for (int i = 0; i < props.length(); i++) {
Object object = props.opt(i);
if (object instanceof JSONObject) {
JSONObject propObj = (JSONObject) object;
String sourceName = propObj.optString("name");
if (sourceName != null) {
Object opt2 = propObj.opt("properties");
List<LiveProperty> properties = parseProperties(sourceName, opt2);
LivePropertySource propertySource = new LivePropertySource(sourceName, properties);
allSources.add(propertySource);
}
}
}
}
return allSources.build();
}
private List<LiveProperty> parseProperties(String sourceName, Object opt2) {
List<LiveProperty> properties = new ArrayList<>();
if (opt2 instanceof JSONObject) {
JSONObject jsonObj = (JSONObject) opt2;
Iterator<?> keys = jsonObj.keys();
if (keys != null) {
while (keys.hasNext()) {
Object key = keys.next();
if (key instanceof String) {
String propKey = (String) key;
Object propContentObj = jsonObj.opt(propKey);
if (propContentObj != null) {
String value = getValue(propContentObj);
LiveProperty property = LiveProperty.builder() //
.source(sourceName) //
.property(propKey) //
.value(value) //
.build();
properties.add(property);
}
}
}
}
}
return properties;
}
private String getValue(Object propContentObj) {
if (propContentObj instanceof JSONObject) {
JSONObject jsonObj = (JSONObject) propContentObj;
return jsonObj.optString("value");
}
return null;
}
}

View File

@@ -0,0 +1,38 @@
/*******************************************************************************
* Copyright (c) 2019 Pivotal, 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
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.commons.boot.app.cli.liveproperties;
import java.util.ArrayList;
import java.util.List;
import com.google.common.collect.ImmutableList;
public class LiveProperties {
private final List<LivePropertySource> sources;
public LiveProperties(List<LivePropertySource> sources) {
this.sources = sources != null ? ImmutableList.copyOf(sources) : ImmutableList.of();
}
public List<LiveProperty> getProperties(String propertyName) {
List<LiveProperty> foundProperties = new ArrayList<>();
for (LivePropertySource source : sources) {
LiveProperty property = source.getProperty(propertyName);
if (property != null) {
foundProperties.add(property);
}
}
return foundProperties;
}
}

View File

@@ -0,0 +1,59 @@
/*******************************************************************************
* Copyright (c) 2019 Pivotal, 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
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.commons.boot.app.cli.liveproperties;
public class LiveProperty {
private String source;
private String property;
private String value;
public String getSource() {
return source;
}
public String getProperty() {
return property;
}
public String getValue() {
return value;
}
public static class LivePropertyBuilder {
private LiveProperty liveProperty = new LiveProperty();
public LivePropertyBuilder source(String source) {
liveProperty.source = source;
return this;
}
public LivePropertyBuilder property(String property) {
liveProperty.property = property;
return this;
}
public LivePropertyBuilder value(String value) {
liveProperty.value = value;
return this;
}
public LiveProperty build() {
return liveProperty;
}
}
public static LivePropertyBuilder builder() {
return new LivePropertyBuilder();
}
}

View File

@@ -0,0 +1,41 @@
/*******************************************************************************
* Copyright (c) 2019 Pivotal, 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
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.commons.boot.app.cli.liveproperties;
import java.util.List;
import com.google.common.collect.ImmutableList;
public class LivePropertySource {
private final List<LiveProperty> properties;
private final String sourceName;
public LivePropertySource(String sourceName, List<LiveProperty> properties) {
this.sourceName = sourceName;
this.properties = properties != null ? ImmutableList.copyOf(properties) : ImmutableList.of();
}
public String getSourceName() {
return this.sourceName;
}
public LiveProperty getProperty(String propertyName) {
for (LiveProperty liveProperty : properties) {
if (liveProperty.getProperty().equals(propertyName)) {
return liveProperty;
}
}
return null;
}
}

View File

@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2017, 2018 Pivotal, Inc.
* Copyright (c) 2017, 2019 Pivotal, 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
@@ -11,7 +11,7 @@
package org.springframework.ide.vscode.boot.java.value;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.eclipse.jdt.core.dom.ASTNode;
@@ -24,10 +24,11 @@ import org.eclipse.jdt.core.dom.TypeDeclaration;
import org.eclipse.lsp4j.Hover;
import org.eclipse.lsp4j.Range;
import org.eclipse.lsp4j.jsonrpc.messages.Either;
import org.json.JSONObject;
import org.springframework.ide.vscode.boot.java.handlers.HoverProvider;
import org.springframework.ide.vscode.boot.java.livehover.LiveHoverUtils;
import org.springframework.ide.vscode.commons.boot.app.cli.SpringBootApp;
import org.springframework.ide.vscode.commons.boot.app.cli.liveproperties.LiveProperties;
import org.springframework.ide.vscode.commons.boot.app.cli.liveproperties.LiveProperty;
import org.springframework.ide.vscode.commons.java.IJavaProject;
import org.springframework.ide.vscode.commons.util.text.TextDocument;
@@ -74,26 +75,19 @@ public class ValueHoverProvider implements HoverProvider {
String propertyKey = value.substring(range.getStart(), range.getEnd());
if (propertyKey != null) {
Map<SpringBootApp, JSONObject> allProperties = getPropertiesFromProcesses(runningApps);
Map<SpringBootApp, LiveProperties> allProperties = getPropertiesFromProcesses(runningApps);
StringBuilder hover = new StringBuilder();
for (SpringBootApp app : allProperties.keySet()) {
JSONObject properties = allProperties.get(app);
Iterator<?> keys = properties.keys();
while (keys.hasNext()) {
String key = (String) keys.next();
if (properties.get(key) instanceof JSONObject) {
JSONObject props = properties.getJSONObject(key);
if (props.has(propertyKey)) {
String propertyValue = props.getString(propertyKey);
hover.append(propertyKey + " : " + propertyValue);
hover.append(" (from: " + key + ")\n\n");
hover.append(LiveHoverUtils.niceAppName(app));
hover.append("\n\n");
}
LiveProperties properties = allProperties.get(app);
List<LiveProperty> foundProperties = properties.getProperties(propertyKey);
if (foundProperties != null) {
for (LiveProperty liveProp : foundProperties) {
hover.append(propertyKey + " : " + liveProp.getValue());
hover.append(" (from: " + liveProp.getSource() + ")\n\n");
hover.append(LiveHoverUtils.niceAppName(app));
hover.append("\n\n");
}
}
}
@@ -115,17 +109,14 @@ public class ValueHoverProvider implements HoverProvider {
return null;
}
public Map<SpringBootApp, JSONObject> getPropertiesFromProcesses(SpringBootApp[] runningApps) {
Map<SpringBootApp, JSONObject> result = new HashMap<>();
public Map<SpringBootApp, LiveProperties> getPropertiesFromProcesses(SpringBootApp[] runningApps) {
Map<SpringBootApp, LiveProperties> result = new HashMap<>();
try {
for (SpringBootApp app : runningApps) {
String environment = app.getEnvironment();
if (environment != null) {
JSONObject env = new JSONObject(environment);
if (env != null) {
result.put(app, env);
}
LiveProperties liveProperties = app.getLiveProperties();
if (liveProperties != null) {
result.put(app, liveProperties);
}
}
}