live hover mechanics, v2

This commit is contained in:
Martin Lippert
2019-09-02 11:00:57 +02:00
parent a89e5a04db
commit e04df5ff2c
61 changed files with 2218 additions and 482 deletions

View File

@@ -42,12 +42,14 @@ import org.json.JSONArray;
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.LiveProperties;
import org.springframework.ide.vscode.commons.boot.app.cli.liveproperties.LivePropertiesJsonParser;
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;
import org.springframework.ide.vscode.boot.java.livehover.v2.Boot1xRequestMapping;
import org.springframework.ide.vscode.boot.java.livehover.v2.LiveBeansModel;
import org.springframework.ide.vscode.boot.java.livehover.v2.LiveConditional;
import org.springframework.ide.vscode.boot.java.livehover.v2.LiveConditionalParser;
import org.springframework.ide.vscode.boot.java.livehover.v2.LiveProperties;
import org.springframework.ide.vscode.boot.java.livehover.v2.LivePropertiesJsonParser;
import org.springframework.ide.vscode.boot.java.livehover.v2.RequestMapping;
import org.springframework.ide.vscode.boot.java.livehover.v2.RequestMappingsParser20;
import org.springframework.ide.vscode.commons.util.AsyncRunner;
import org.springframework.ide.vscode.commons.util.ExceptionUtil;
import org.springframework.ide.vscode.commons.util.FuctionWithException;

View File

@@ -15,9 +15,10 @@ import java.util.List;
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 org.springframework.ide.vscode.boot.java.livehover.v2.LiveBeansModel;
import org.springframework.ide.vscode.boot.java.livehover.v2.LiveConditional;
import org.springframework.ide.vscode.boot.java.livehover.v2.LiveProperties;
import org.springframework.ide.vscode.boot.java.livehover.v2.RequestMapping;
import reactor.core.Disposable;

View File

@@ -30,9 +30,9 @@ public class SpringBootAppCLI {
System.out.println("Spring Boot App: " + app.getProcessID());
System.out.println("Name: " + app.getProcessName());
System.out.println("Port: " + app.getPort());
System.out.println("Beans: " + app.getBeans());
System.out.println("Mappings: " + app.getRequestMappings());
System.out.println("ConfigReport: " + app.getLiveConditionals());
// System.out.println("Beans: " + app.getBeans());
// System.out.println("Mappings: " + app.getRequestMappings());
// System.out.println("ConfigReport: " + app.getLiveConditionals());
System.out.println();
}

View File

@@ -13,8 +13,8 @@ package org.springframework.ide.vscode.commons.boot.app.cli;
import static org.junit.Assert.assertEquals;
import org.junit.Test;
import org.springframework.ide.vscode.commons.boot.app.cli.requestmappings.AbstractRequestMapping;
import org.springframework.ide.vscode.commons.boot.app.cli.requestmappings.Boot1xRequestMapping;
import org.springframework.ide.vscode.boot.java.livehover.v2.AbstractRequestMapping;
import org.springframework.ide.vscode.boot.java.livehover.v2.Boot1xRequestMapping;
/**
* @author Martin Lippert

View File

@@ -19,8 +19,8 @@ import java.util.stream.Collectors;
import org.apache.commons.io.IOUtils;
import org.json.JSONObject;
import org.junit.Test;
import org.springframework.ide.vscode.commons.boot.app.cli.requestmappings.RequestMapping;
import org.springframework.ide.vscode.commons.boot.app.cli.requestmappings.RequestMappingsParser20;
import org.springframework.ide.vscode.boot.java.livehover.v2.RequestMapping;
import org.springframework.ide.vscode.boot.java.livehover.v2.RequestMappingsParser20;
import com.google.common.collect.ImmutableSet;

View File

@@ -19,8 +19,8 @@ import java.util.List;
import org.apache.commons.io.IOUtils;
import org.junit.Test;
import org.springframework.ide.vscode.commons.boot.app.cli.livebean.LiveBean;
import org.springframework.ide.vscode.commons.boot.app.cli.livebean.LiveBeansModel;
import org.springframework.ide.vscode.boot.java.livehover.v2.LiveBean;
import org.springframework.ide.vscode.boot.java.livehover.v2.LiveBeansModel;
/**
* @author Martin Lippert

View File

@@ -30,8 +30,9 @@ import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;
import org.springframework.ide.vscode.commons.boot.app.cli.livebean.LiveBeansModel;
import org.springframework.ide.vscode.commons.boot.app.cli.requestmappings.RequestMapping;
import org.springframework.ide.vscode.boot.java.livehover.v2.LiveBeansModel;
import org.springframework.ide.vscode.boot.java.livehover.v2.LiveConditional;
import org.springframework.ide.vscode.boot.java.livehover.v2.RequestMapping;
import org.springframework.ide.vscode.commons.util.AsyncProcess;
import org.springframework.ide.vscode.commons.util.ExceptionUtil;
import org.springframework.ide.vscode.commons.util.ExternalCommand;

View File

@@ -43,6 +43,12 @@ import org.springframework.ide.vscode.boot.java.links.SourceLinks;
import org.springframework.ide.vscode.boot.java.livehover.ActiveProfilesProvider;
import org.springframework.ide.vscode.boot.java.livehover.BeanInjectedIntoHoverProvider;
import org.springframework.ide.vscode.boot.java.livehover.ComponentInjectionsHoverProvider;
import org.springframework.ide.vscode.boot.java.livehover.v2.SpringProcessConnectorLocal;
import org.springframework.ide.vscode.boot.java.livehover.v2.SpringProcessConnectorRemote;
import org.springframework.ide.vscode.boot.java.livehover.v2.SpringProcessConnectorService;
import org.springframework.ide.vscode.boot.java.livehover.v2.SpringProcessLiveDataProvider;
import org.springframework.ide.vscode.boot.java.livehover.v2.SpringProcessLiveHoverUpdater;
import org.springframework.ide.vscode.boot.java.livehover.v2.SpringProcessTracker;
import org.springframework.ide.vscode.boot.java.requestmapping.LiveAppURLSymbolProvider;
import org.springframework.ide.vscode.boot.java.requestmapping.RequestMappingHoverProvider;
import org.springframework.ide.vscode.boot.java.requestmapping.WebfluxHandlerCodeLensProvider;
@@ -53,7 +59,6 @@ import org.springframework.ide.vscode.boot.java.snippets.JavaSnippetContext;
import org.springframework.ide.vscode.boot.java.snippets.JavaSnippetManager;
import org.springframework.ide.vscode.boot.java.utils.CompilationUnitCache;
import org.springframework.ide.vscode.boot.java.utils.SpringLiveChangeDetectionWatchdog;
import org.springframework.ide.vscode.boot.java.utils.SpringLiveHoverWatchdog;
import org.springframework.ide.vscode.boot.java.utils.SymbolCache;
import org.springframework.ide.vscode.boot.java.value.ValueCompletionProcessor;
import org.springframework.ide.vscode.boot.java.value.ValueHoverProvider;
@@ -73,7 +78,6 @@ import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguage
import org.springframework.ide.vscode.commons.languageserver.util.SimpleTextDocumentService;
import org.springframework.ide.vscode.commons.languageserver.util.SimpleWorkspaceService;
import org.springframework.ide.vscode.commons.util.text.LanguageId;
import org.springframework.ide.vscode.commons.util.text.TextDocument;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
@@ -100,7 +104,10 @@ public class BootJavaLanguageServerComponents implements LanguageServerComponent
private final BootLanguageServerParams serverParams;
private final SpringPropertyIndexProvider propertyIndexProvider;
private final ProjectBasedPropertyIndexProvider adHocPropertyIndexProvider;
private final SpringLiveHoverWatchdog liveHoverWatchdog;
private final SpringProcessLiveDataProvider liveDataProvider;
private final SpringProcessConnectorService liveDataConnector;
private final SpringLiveChangeDetectionWatchdog liveChangeDetectionWatchdog;
private final ProjectObserver projectObserver;
private final CompilationUnitCache cuCache;
@@ -110,6 +117,9 @@ public class BootJavaLanguageServerComponents implements LanguageServerComponent
private CodeLensHandler codeLensHandler;
private DocumentHighlightHandler highlightsEngine;
private SpringProcessTracker liveProcessTracker;
public BootJavaLanguageServerComponents(
SimpleLanguageServer server,
BootLanguageServerParams serverParams,
@@ -142,37 +152,44 @@ public class BootJavaLanguageServerComponents implements LanguageServerComponent
workspaceService.onWorkspaceSymbol(new BootJavaWorkspaceSymbolHandler(indexer,
new LiveAppURLSymbolProvider(runningAppProvider)));
// BootJavaCodeLensEngine codeLensHandler = createCodeLensEngine(server, projectFinder);
// documents.onCodeLens(codeLensHandler::createCodeLenses);
// documents.onCodeLensResolve(codeLensHandler::resolveCodeLens);
hoverProvider = createHoverHandler(projectFinder, runningAppProvider, sourceLinks);
liveHoverWatchdog = new SpringLiveHoverWatchdog(server, hoverProvider, runningAppProvider,
projectFinder, projectObserver, serverParams.watchDogInterval);
documents.onDidChangeContent(params -> {
TextDocument doc = params.getDocument();
if (getInterestingLanguages().contains(doc.getLanguageId())) {
liveDataProvider = new SpringProcessLiveDataProvider();
hoverProvider = createHoverHandler(projectFinder, sourceLinks, liveDataProvider);
new SpringProcessLiveHoverUpdater(server, hoverProvider, projectFinder, liveDataProvider);
liveDataConnector = new SpringProcessConnectorService();
new SpringProcessConnectorRemote(server, liveDataConnector, liveDataProvider);
SpringProcessConnectorLocal liveHoverLocalProcessConnector = new SpringProcessConnectorLocal(liveDataConnector, liveDataProvider, projectObserver);
liveProcessTracker = new SpringProcessTracker(liveHoverLocalProcessConnector, serverParams.watchDogInterval);
// liveHoverWatchdog = new SpringLiveHoverWatchdog(server, hoverProvider, runningAppProvider,
// projectFinder, projectObserver, serverParams.watchDogInterval);
// documents.onDidChangeContent(params -> {
// TextDocument doc = params.getDocument();
// if (getInterestingLanguages().contains(doc.getLanguageId())) {
// if (testHightlighter != null) {
// getClient()
// .highlight(new HighlightParams(params.getDocument().getId(), testHightlighter.apply(doc)));
// } else {
try {
liveHoverWatchdog.watchDocument(doc.getUri());
liveHoverWatchdog.update(doc.getUri());
} catch (Throwable t) {
log.error("", t);
}
// try {
// liveHoverWatchdog.watchDocument(doc.getUri());
// liveHoverWatchdog.update(doc.getUri());
// } catch (Throwable t) {
// log.error("", t);
// }
// }
}
});
// }
// });
documents.onDidClose(doc -> {
// documents.onDidClose(doc -> {
// if (testHightlighter != null) {
// getClient().highlight(new HighlightParams(doc.getId(), testHightlighter.apply(doc)));
// } else {
liveHoverWatchdog.unwatchDocument(doc.getUri());
// liveHoverWatchdog.unwatchDocument(doc.getUri());
// }
});
// });
liveChangeDetectionWatchdog = new SpringLiveChangeDetectionWatchdog(
this,
@@ -191,11 +208,12 @@ public class BootJavaLanguageServerComponents implements LanguageServerComponent
config.addListener(ignore -> {
// live hover watchdog
if (config.isBootHintsEnabled()) {
liveHoverWatchdog.enableHighlights();
} else {
liveHoverWatchdog.disableHighlights();
}
// liveProcessTracker.setTrackingEnabled(config.isBootHintsEnabled());
// if (config.isBootHintsEnabled()) {
// liveHoverWatchdog.enableHighlights();
// } else {
// liveHoverWatchdog.disableHighlights();
// }
// live change detection watchdog
if (config.isChangeDetectionEnabled()) {
@@ -233,11 +251,13 @@ public class BootJavaLanguageServerComponents implements LanguageServerComponent
}
private void initialized() {
this.liveProcessTracker.start();
this.liveChangeDetectionWatchdog.start();
}
private void shutdown() {
this.liveHoverWatchdog.shutdown();
// this.liveHoverWatchdog.shutdown();
this.liveProcessTracker.stop();
this.liveChangeDetectionWatchdog.shutdown();
this.cuCache.dispose();
}
@@ -300,8 +320,9 @@ public class BootJavaLanguageServerComponents implements LanguageServerComponent
return snippetManager;
}
protected BootJavaHoverProvider createHoverHandler(JavaProjectFinder javaProjectFinder,
RunningAppProvider runningAppProvider, SourceLinks sourceLinks) {
protected BootJavaHoverProvider createHoverHandler(JavaProjectFinder javaProjectFinder, SourceLinks sourceLinks,
SpringProcessLiveDataProvider liveDataProvider) {
AnnotationHierarchyAwareLookup<HoverProvider> providers = new AnnotationHierarchyAwareLookup<>();
ValueHoverProvider valueHoverProvider = new ValueHoverProvider();
@@ -345,7 +366,7 @@ public class BootJavaLanguageServerComponents implements LanguageServerComponent
providers.put(Annotations.CONDITIONAL_ON_JNDI, conditionalsLiveHoverProvider);
providers.put(Annotations.CONDITIONAL_ON_SINGLE_CANDIDATE, conditionalsLiveHoverProvider);
return new BootJavaHoverProvider(this, javaProjectFinder, providers, runningAppProvider);
return new BootJavaHoverProvider(this, javaProjectFinder, providers, liveDataProvider);
}
protected ReferencesHandler createReferenceHandler(SimpleLanguageServer server, JavaProjectFinder projectFinder) {

View File

@@ -38,9 +38,9 @@ import org.springframework.ide.vscode.boot.java.handlers.HoverProvider;
import org.springframework.ide.vscode.boot.java.links.SourceLinks;
import org.springframework.ide.vscode.boot.java.livehover.ComponentInjectionsHoverProvider;
import org.springframework.ide.vscode.boot.java.livehover.LiveHoverUtils;
import org.springframework.ide.vscode.boot.java.livehover.v2.LiveBean;
import org.springframework.ide.vscode.boot.java.livehover.v2.SpringProcessLiveData;
import org.springframework.ide.vscode.boot.java.utils.ASTUtils;
import org.springframework.ide.vscode.commons.boot.app.cli.SpringBootApp;
import org.springframework.ide.vscode.commons.boot.app.cli.livebean.LiveBean;
import org.springframework.ide.vscode.commons.java.IJavaProject;
import org.springframework.ide.vscode.commons.java.IType;
import org.springframework.ide.vscode.commons.util.BadLocationException;
@@ -73,17 +73,17 @@ public class AutowiredHoverProvider implements HoverProvider {
@Override
public Collection<CodeLens> getLiveHintCodeLenses(IJavaProject project, Annotation annotation, TextDocument doc,
SpringBootApp[] runningApps) {
SpringProcessLiveData[] processLiveData) {
ImmutableList.Builder<CodeLens> builder = ImmutableList.builder();
if (runningApps.length > 0) {
if (processLiveData.length > 0) {
LiveBean definedBean = getDefinedBeanForTypeDeclaration(ASTUtils.findDeclaringType(annotation));
// Annotation is MarkerNode, parent is some field, method, variable declaration
// node.
ASTNode declarationNode = annotation.getParent();
try {
Range hoverRange = doc.toRange(annotation.getStartPosition(), annotation.getLength());
for (SpringBootApp app : runningApps) {
List<LiveBean> relevantBeans = getRelevantAutowiredBeans(project, declarationNode, app,
for (SpringProcessLiveData liveData : processLiveData) {
List<LiveBean> relevantBeans = getRelevantAutowiredBeans(project, declarationNode, liveData,
definedBean);
if (!relevantBeans.isEmpty()) {
builder.addAll(LiveHoverUtils.createCodeLensesForBeans(hoverRange, relevantBeans,
@@ -100,12 +100,12 @@ public class AutowiredHoverProvider implements HoverProvider {
@Override
public Hover provideHover(ASTNode node, Annotation annotation, ITypeBinding type, int offset,
TextDocument doc, IJavaProject project, SpringBootApp[] runningApps) {
if (runningApps.length > 0) {
TextDocument doc, IJavaProject project, SpringProcessLiveData[] processLiveData) {
if (processLiveData.length > 0) {
LiveBean definedBean = getDefinedBeanForTypeDeclaration(ASTUtils.findDeclaringType(annotation));
// Annotation is MarkerNode, parent is some field, method, variable declaration node.
ASTNode declarationNode = annotation.getParent();
Hover hover = provideHover(definedBean, declarationNode, offset, doc, project, runningApps);
Hover hover = provideHover(definedBean, declarationNode, offset, doc, project, processLiveData);
if (hover != null) {
try {
hover.setRange(doc.toRange(annotation.getStartPosition(), annotation.getLength()));
@@ -119,14 +119,14 @@ public class AutowiredHoverProvider implements HoverProvider {
}
private Hover provideHover(LiveBean definedBean, ASTNode declarationNode, int offset, TextDocument doc,
IJavaProject project, SpringBootApp[] runningApps) {
IJavaProject project, SpringProcessLiveData[] processLiveData) {
if (definedBean != null) {
StringBuilder hover = new StringBuilder();
for (SpringBootApp app : runningApps) {
for (SpringProcessLiveData liveData : processLiveData) {
List<LiveBean> autowiredBeans = getRelevantAutowiredBeans(project, declarationNode, app, definedBean);
List<LiveBean> autowiredBeans = getRelevantAutowiredBeans(project, declarationNode, liveData, definedBean);
if (!autowiredBeans.isEmpty()) {
if (hover.length() > 0) {
@@ -136,7 +136,7 @@ public class AutowiredHoverProvider implements HoverProvider {
hover.append("Bean id: `");
hover.append(definedBean.getId());
hover.append("` \n");
hover.append(LiveHoverUtils.niceAppName(app));
hover.append(LiveHoverUtils.niceAppName(liveData));
}
}
@@ -158,14 +158,14 @@ public class AutowiredHoverProvider implements HoverProvider {
hover.append("\n \n");
}
public static List<LiveBean> getRelevantAutowiredBeans(IJavaProject project, ASTNode declarationNode, SpringBootApp app, LiveBean definedBean) {
List<LiveBean> relevantBeans = LiveHoverUtils.findRelevantBeans(app, definedBean);
return getRelevantAutowiredBeans(project, declarationNode, app, relevantBeans);
public static List<LiveBean> getRelevantAutowiredBeans(IJavaProject project, ASTNode declarationNode, SpringProcessLiveData liveData, LiveBean definedBean) {
List<LiveBean> relevantBeans = LiveHoverUtils.findRelevantBeans(liveData, definedBean);
return getRelevantAutowiredBeans(project, declarationNode, liveData, relevantBeans);
}
public static List<LiveBean> getRelevantAutowiredBeans(IJavaProject project, ASTNode declarationNode, SpringBootApp app, List<LiveBean> relevantBeans) {
public static List<LiveBean> getRelevantAutowiredBeans(IJavaProject project, ASTNode declarationNode, SpringProcessLiveData liveData, List<LiveBean> relevantBeans) {
if (!relevantBeans.isEmpty()) {
List<LiveBean> allDependencyBeans = LiveHoverUtils.findAllDependencyBeans(app, relevantBeans);
List<LiveBean> allDependencyBeans = LiveHoverUtils.findAllDependencyBeans(liveData, relevantBeans);
if (!allDependencyBeans.isEmpty()) {
@@ -304,9 +304,9 @@ public class AutowiredHoverProvider implements HoverProvider {
}
@Override
public Hover provideHover(MethodDeclaration methodDeclaration, int offset, TextDocument doc, IJavaProject project, SpringBootApp[] runningApps) {
public Hover provideHover(MethodDeclaration methodDeclaration, int offset, TextDocument doc, IJavaProject project, SpringProcessLiveData[] processLiveData) {
LiveBean definedBean = getDefinedBeanForImplicitAutowiredConstructor(methodDeclaration);
Hover hover = provideHover(definedBean, methodDeclaration, offset, doc, project, runningApps);
Hover hover = provideHover(definedBean, methodDeclaration, offset, doc, project, processLiveData);
if (hover != null) {
SimpleName name = methodDeclaration.getName();
try {
@@ -320,10 +320,10 @@ public class AutowiredHoverProvider implements HoverProvider {
@Override
public Hover provideMethodParameterHover(SingleVariableDeclaration parameter, int offset, TextDocument doc,
IJavaProject project, SpringBootApp[] runningApps) {
IJavaProject project, SpringProcessLiveData[] processLiveData) {
MethodDeclaration method = (MethodDeclaration) parameter.getParent();
LiveBean definedBean = getDefinedBeanForImplicitAutowiredConstructor(method);
Hover hover = provideHover(definedBean, parameter, offset, doc, project, runningApps);
Hover hover = provideHover(definedBean, parameter, offset, doc, project, processLiveData);
if (hover != null) {
SimpleName name = parameter.getName();
try {
@@ -337,7 +337,7 @@ public class AutowiredHoverProvider implements HoverProvider {
@Override
public Collection<CodeLens> getLiveHintCodeLenses(IJavaProject project, MethodDeclaration methodDeclaration,
TextDocument doc, SpringBootApp[] runningApps) {
TextDocument doc, SpringProcessLiveData[] processLiveData) {
ImmutableList.Builder<CodeLens> builder = ImmutableList.builder();
LiveBean definedBean = getDefinedBeanForImplicitAutowiredConstructor(methodDeclaration);
if (definedBean != null) {
@@ -345,8 +345,8 @@ public class AutowiredHoverProvider implements HoverProvider {
Range hoverRange = doc.toRange(methodDeclaration.getName().getStartPosition(),
methodDeclaration.getName().getLength());
for (SpringBootApp app : runningApps) {
List<LiveBean> relevantBeans = getRelevantAutowiredBeans(project, methodDeclaration, app,
for (SpringProcessLiveData liveData : processLiveData) {
List<LiveBean> relevantBeans = getRelevantAutowiredBeans(project, methodDeclaration, liveData,
definedBean);
if (!relevantBeans.isEmpty()) {
@@ -357,7 +357,7 @@ public class AutowiredHoverProvider implements HoverProvider {
// CodeLenses for the method parameters. Only ranges just to provide a highlight
// for the hover
builder.addAll(LiveHoverUtils.createCodeLensForMethodParameters(app, project, methodDeclaration, doc, relevantBeans));
builder.addAll(LiveHoverUtils.createCodeLensForMethodParameters(liveData, project, methodDeclaration, doc, relevantBeans));
}
}

View File

@@ -11,6 +11,7 @@
package org.springframework.ide.vscode.boot.java.conditionals;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
@@ -30,8 +31,8 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.LiveConditional;
import org.springframework.ide.vscode.commons.boot.app.cli.SpringBootApp;
import org.springframework.ide.vscode.boot.java.livehover.v2.LiveConditional;
import org.springframework.ide.vscode.boot.java.livehover.v2.SpringProcessLiveData;
import org.springframework.ide.vscode.commons.java.IJavaProject;
import org.springframework.ide.vscode.commons.util.text.TextDocument;
@@ -48,14 +49,14 @@ public class ConditionalsLiveHoverProvider implements HoverProvider {
@Override
public Hover provideHover(ASTNode node, Annotation annotation, ITypeBinding type, int offset,
TextDocument doc, IJavaProject project, SpringBootApp[] runningApps) {
return provideHover(annotation, doc, runningApps);
TextDocument doc, IJavaProject project, SpringProcessLiveData[] processLiveData) {
return provideHover(annotation, doc, processLiveData);
}
@Override
public Collection<CodeLens> getLiveHintCodeLenses(IJavaProject project, Annotation annotation, TextDocument doc, SpringBootApp[] runningApps) {
public Collection<CodeLens> getLiveHintCodeLenses(IJavaProject project, Annotation annotation, TextDocument doc, SpringProcessLiveData[] processLiveData) {
try {
Optional<List<LiveConditional>> val = getMatchedLiveConditionals(annotation, runningApps);
Optional<List<LiveConditional>> val = getMatchedLiveConditionals(annotation, processLiveData);
if (val.isPresent()) {
Range hoverRange = doc.toRange(annotation.getStartPosition(), annotation.getLength());
return ImmutableList.of(new CodeLens(hoverRange));
@@ -68,18 +69,19 @@ public class ConditionalsLiveHoverProvider implements HoverProvider {
}
private Optional<List<LiveConditional>> getMatchedLiveConditionals(Annotation annotation,
SpringBootApp[] runningApps) throws Exception {
if (runningApps != null) {
SpringProcessLiveData[] processLiveData) throws Exception {
if (processLiveData != null) {
List<LiveConditional> all = new ArrayList<>();
for (SpringBootApp springBootApp : runningApps) {
springBootApp.getLiveConditionals().ifPresent((conditionals) -> {
conditionals.stream().forEach((conditional) -> {
for (SpringProcessLiveData liveData : processLiveData) {
LiveConditional[] conditionals = liveData.getLiveConditionals();
if (conditionals != null) {
Arrays.stream(conditionals).forEach((conditional) -> {
if (matchesAnnotation(annotation, conditional)) {
all.add(conditional);
}
});
});
};
}
if (!all.isEmpty()) {
return Optional.of(all);
@@ -89,11 +91,11 @@ public class ConditionalsLiveHoverProvider implements HoverProvider {
}
private Hover provideHover(Annotation annotation, TextDocument doc,
SpringBootApp[] runningApps) {
SpringProcessLiveData[] processLiveData) {
try {
List<Either<String, MarkedString>> hoverContent = new ArrayList<>();
Optional<List<LiveConditional>> val = getMatchedLiveConditionals(annotation, runningApps);
Optional<List<LiveConditional>> val = getMatchedLiveConditionals(annotation, processLiveData);
if (val.isPresent()) {
addHoverContent(val.get(), hoverContent);

View File

@@ -36,15 +36,15 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ide.vscode.boot.java.BootJavaLanguageServerComponents;
import org.springframework.ide.vscode.boot.java.annotations.AnnotationHierarchyAwareLookup;
import org.springframework.ide.vscode.boot.java.livehover.v2.SpringProcessLiveData;
import org.springframework.ide.vscode.boot.java.livehover.v2.SpringProcessLiveDataProvider;
import org.springframework.ide.vscode.boot.java.utils.ASTUtils;
import org.springframework.ide.vscode.commons.boot.app.cli.SpringBootApp;
import org.springframework.ide.vscode.commons.java.IClasspath;
import org.springframework.ide.vscode.commons.java.IClasspathUtil;
import org.springframework.ide.vscode.commons.java.IJavaProject;
import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder;
import org.springframework.ide.vscode.commons.languageserver.util.HoverHandler;
import org.springframework.ide.vscode.commons.languageserver.util.SimpleTextDocumentService;
import org.springframework.ide.vscode.commons.protocol.java.Classpath;
import org.springframework.ide.vscode.commons.util.text.DocumentRegion;
import org.springframework.ide.vscode.commons.util.text.IDocument;
import org.springframework.ide.vscode.commons.util.text.TextDocument;
@@ -58,23 +58,19 @@ public class BootJavaHoverProvider implements HoverHandler {
private static Logger logger = LoggerFactory.getLogger(BootJavaHoverProvider.class);
private JavaProjectFinder projectFinder;
private BootJavaLanguageServerComponents server;
private AnnotationHierarchyAwareLookup<HoverProvider> hoverProviders;
private RunningAppProvider runningAppProvider;
private Collection<SpringBootApp> runningSpringBootApps;
private final JavaProjectFinder projectFinder;
private final BootJavaLanguageServerComponents server;
private final AnnotationHierarchyAwareLookup<HoverProvider> hoverProviders;
private final SpringProcessLiveDataProvider liveDataProvider;
public BootJavaHoverProvider(BootJavaLanguageServerComponents server, JavaProjectFinder projectFinder,
AnnotationHierarchyAwareLookup<HoverProvider> specificProviders, RunningAppProvider runningAppProvider) {
AnnotationHierarchyAwareLookup<HoverProvider> specificProviders, SpringProcessLiveDataProvider liveDataProvider) {
this.server = server;
this.projectFinder = projectFinder;
this.hoverProviders = specificProviders;
this.runningAppProvider = runningAppProvider;
this.runningSpringBootApps = null;
this.liveDataProvider = liveDataProvider;
}
@Override
public Hover handle(TextDocumentPositionParams params) {
SimpleTextDocumentService documents = server.getTextDocumentService();
@@ -97,8 +93,10 @@ public class BootJavaHoverProvider implements HoverHandler {
return SimpleTextDocumentService.NO_HOVER;
}
public CodeLens[] getLiveHoverHints(final TextDocument document, IJavaProject project, final SpringBootApp[] runningBootApps) {
if (runningBootApps.length == 0) return new CodeLens[0];
public CodeLens[] getLiveHoverHints(final TextDocument document, final IJavaProject project) {
final SpringProcessLiveData[] processLiveData = this.liveDataProvider.getLatestLiveData();
if (processLiveData.length == 0) return new CodeLens[0];
if (project == null) return new CodeLens[0];
return server.getCompilationUnitCache().withCompilationUnit(project, URI.create(document.getUri()), cu -> {
@@ -110,7 +108,7 @@ public class BootJavaHoverProvider implements HoverHandler {
@Override
public boolean visit(TypeDeclaration node) {
try {
extractLiveHintsForType(node, document, project, runningBootApps, result);
extractLiveHintsForType(node, document, project, processLiveData, result);
}
catch (Exception e) {
logger.error("error extracting live hint information for docURI '" + document.getUri() + "' - on node: " + node.toString(), e);
@@ -121,7 +119,7 @@ public class BootJavaHoverProvider implements HoverHandler {
@Override
public boolean visit(SingleMemberAnnotation node) {
try {
extractLiveHintsForAnnotation(node, document, project, runningBootApps, result);
extractLiveHintsForAnnotation(node, document, project, processLiveData, result);
} catch (Exception e) {
logger.error("error extracting live hint information for docURI '" + document.getUri() + "' - on node: " + node.toString(), e);
}
@@ -132,7 +130,7 @@ public class BootJavaHoverProvider implements HoverHandler {
@Override
public boolean visit(NormalAnnotation node) {
try {
extractLiveHintsForAnnotation(node, document, project, runningBootApps, result);
extractLiveHintsForAnnotation(node, document, project, processLiveData, result);
} catch (Exception e) {
logger.error("error extracting live hint information for docURI '" + document.getUri() + "' - on node: " + node.toString(), e);
}
@@ -143,7 +141,7 @@ public class BootJavaHoverProvider implements HoverHandler {
@Override
public boolean visit(MarkerAnnotation node) {
try {
extractLiveHintsForAnnotation(node, document, project, runningBootApps, result);
extractLiveHintsForAnnotation(node, document, project, processLiveData, result);
} catch (Exception e) {
logger.error("error extracting live hint information for docURI '" + document.getUri() + "' - on node: " + node.toString(), e);
}
@@ -154,7 +152,7 @@ public class BootJavaHoverProvider implements HoverHandler {
@Override
public boolean visit(MethodDeclaration node) {
try {
extractLiveHintsForMethod(node, document, project, runningBootApps, result);
extractLiveHintsForMethod(node, document, project, processLiveData, result);
} catch (Exception e) {
logger.error("error extracting live hint information for docURI '" + document.getUri() + "' - on node: " + node.toString(), e);
}
@@ -173,10 +171,10 @@ public class BootJavaHoverProvider implements HoverHandler {
}
protected void extractLiveHintsForMethod(MethodDeclaration methodDeclaration, TextDocument doc, IJavaProject project,
SpringBootApp[] runningApps, Collection<CodeLens> result) {
SpringProcessLiveData[] processLiveData, Collection<CodeLens> result) {
Collection<HoverProvider> providers = this.hoverProviders.getAll();
for (HoverProvider provider : providers) {
Collection<CodeLens> hints = provider.getLiveHintCodeLenses(project, methodDeclaration, doc, runningApps);
Collection<CodeLens> hints = provider.getLiveHintCodeLenses(project, methodDeclaration, doc, processLiveData);
if (hints!=null) {
result.addAll(hints);
}
@@ -184,10 +182,10 @@ public class BootJavaHoverProvider implements HoverHandler {
}
protected void extractLiveHintsForType(TypeDeclaration typeDeclaration, TextDocument doc, IJavaProject project,
SpringBootApp[] runningApps, Collection<CodeLens> result) {
SpringProcessLiveData[] processLiveData, Collection<CodeLens> result) {
Collection<HoverProvider> providers = this.hoverProviders.getAll();
for (HoverProvider provider : providers) {
Collection<CodeLens> hints = provider.getLiveHintCodeLenses(project, typeDeclaration, doc, runningApps);
Collection<CodeLens> hints = provider.getLiveHintCodeLenses(project, typeDeclaration, doc, processLiveData);
if (hints!=null) {
result.addAll(hints);
}
@@ -195,11 +193,11 @@ public class BootJavaHoverProvider implements HoverHandler {
}
protected void extractLiveHintsForAnnotation(Annotation annotation, TextDocument doc, IJavaProject project,
SpringBootApp[] runningApps, Collection<CodeLens> result) {
SpringProcessLiveData[] processLiveData, Collection<CodeLens> result) {
ITypeBinding type = annotation.resolveTypeBinding();
if (type != null) {
for (HoverProvider provider : this.hoverProviders.get(type)) {
Collection<CodeLens> hints = provider.getLiveHintCodeLenses(project, annotation, doc, runningApps);
Collection<CodeLens> hints = provider.getLiveHintCodeLenses(project, annotation, doc, processLiveData);
if (hints!=null) {
result.addAll(hints);
}
@@ -208,12 +206,14 @@ public class BootJavaHoverProvider implements HoverHandler {
}
private Hover provideHover(TextDocument document, int offset) throws Exception {
final SpringProcessLiveData[] processLiveData = this.liveDataProvider.getLatestLiveData();
IJavaProject project = getProject(document).orElse(null);
if (project != null) {
return server.getCompilationUnitCache().withCompilationUnit(project, URI.create(document.getUri()), cu -> {
ASTNode node = NodeFinder.perform(cu, offset, 0);
if (node != null) {
return provideHover(node, offset, document, project);
return provideHover(node, offset, document, project, processLiveData);
}
return null;
});
@@ -221,7 +221,7 @@ public class BootJavaHoverProvider implements HoverHandler {
return null;
}
private Hover provideHover(ASTNode node, int offset, TextDocument doc, IJavaProject project) {
private Hover provideHover(ASTNode node, int offset, TextDocument doc, IJavaProject project, SpringProcessLiveData[] processLiveData) {
// look for spring annotations first
ASTNode annotationNode = node;
@@ -229,30 +229,28 @@ public class BootJavaHoverProvider implements HoverHandler {
annotationNode = annotationNode.getParent();
}
if (annotationNode != null) {
return provideHoverForAnnotation(node, (Annotation) annotationNode, offset, doc, project);
return provideHoverForAnnotation(node, (Annotation) annotationNode, offset, doc, project, processLiveData);
}
// then do additional AST node coverage
if (node instanceof SimpleName) {
ASTNode parent = node.getParent();
if (parent instanceof TypeDeclaration) {
return provideHoverForTypeDeclaration(node, (TypeDeclaration) parent, offset, doc, project);
return provideHoverForTypeDeclaration(node, (TypeDeclaration) parent, offset, doc, project, processLiveData);
} else if (parent instanceof MethodDeclaration) {
return provideHoverForMethodDeclaration((MethodDeclaration) parent, offset, doc, project);
return provideHoverForMethodDeclaration((MethodDeclaration) parent, offset, doc, project, processLiveData);
} else if (parent instanceof SingleVariableDeclaration && parent.getParent() instanceof MethodDeclaration) {
return provideHoverForMethodParameter((SingleVariableDeclaration) parent, offset, doc, project);
return provideHoverForMethodParameter((SingleVariableDeclaration) parent, offset, doc, project, processLiveData);
}
}
return null;
}
private Hover provideHoverForMethodParameter(SingleVariableDeclaration parameter, int offset, TextDocument doc,
IJavaProject project) {
SpringBootApp[] runningApps = getRunningSpringApps(project);
if (runningApps.length > 0) {
IJavaProject project, SpringProcessLiveData[] processLiveData) {
if (processLiveData.length > 0) {
for (HoverProvider provider : this.hoverProviders.getAll()) {
Hover hover = provider.provideMethodParameterHover(parameter, offset, doc, project, runningApps);
Hover hover = provider.provideMethodParameterHover(parameter, offset, doc, project, processLiveData);
if (hover != null) {
return hover;
}
@@ -262,12 +260,10 @@ public class BootJavaHoverProvider implements HoverHandler {
}
private Hover provideHoverForMethodDeclaration(MethodDeclaration methodDeclaration, int offset, TextDocument doc,
IJavaProject project) {
SpringBootApp[] runningApps = getRunningSpringApps(project);
if (runningApps.length > 0) {
IJavaProject project, SpringProcessLiveData[] processLiveData) {
if (processLiveData.length > 0) {
for (HoverProvider provider : this.hoverProviders.getAll()) {
Hover hover = provider.provideHover(methodDeclaration, offset, doc, project, runningApps);
Hover hover = provider.provideHover(methodDeclaration, offset, doc, project, processLiveData);
if (hover != null) {
//TODO: compose multiple hovers somehow instead of just returning the first one?
return hover;
@@ -277,16 +273,16 @@ public class BootJavaHoverProvider implements HoverHandler {
return null;
}
private Hover provideHoverForAnnotation(ASTNode exactNode, Annotation annotation, int offset, TextDocument doc, IJavaProject project) {
private Hover provideHoverForAnnotation(ASTNode exactNode, Annotation annotation, int offset, TextDocument doc, IJavaProject project,
SpringProcessLiveData[] processLiveData) {
ITypeBinding type = annotation.resolveTypeBinding();
if (type != null) {
logger.debug("Hover requested for "+type.getName());
SpringBootApp[] runningApps = getRunningSpringApps(project);
if (runningApps.length > 0) {
if (processLiveData.length > 0) {
for (HoverProvider provider : this.hoverProviders.get(type)) {
Hover hover = provider.provideHover(exactNode, annotation, type, offset, doc, project, runningApps);
Hover hover = provider.provideHover(exactNode, annotation, type, offset, doc, project, processLiveData);
if (hover != null) {
logger.debug("Hover found: "+hover);
//TODO: compose multiple hovers somehow instead of just returning the first one?
@@ -307,13 +303,13 @@ public class BootJavaHoverProvider implements HoverHandler {
return null;
}
private Hover provideHoverForTypeDeclaration(ASTNode exactNode, TypeDeclaration typeDeclaration, int offset, TextDocument doc, IJavaProject project) {
SpringBootApp[] runningApps = getRunningSpringApps(project);
if (runningApps.length > 0) {
private Hover provideHoverForTypeDeclaration(ASTNode exactNode, TypeDeclaration typeDeclaration, int offset, TextDocument doc,
IJavaProject project, SpringProcessLiveData[] processLiveData) {
if (processLiveData.length > 0) {
ITypeBinding type = typeDeclaration.resolveBinding();
for (HoverProvider provider : this.hoverProviders.getAll()) {
Hover hover = provider.provideHover(exactNode, typeDeclaration, type, offset, doc, project, runningApps);
Hover hover = provider.provideHover(exactNode, typeDeclaration, type, offset, doc, project, processLiveData);
if (hover!=null) {
//TODO: compose multiple hovers somehow instead of just returning the first one?
return hover;
@@ -351,24 +347,4 @@ public class BootJavaHoverProvider implements HoverHandler {
return this.projectFinder.find(new TextDocumentIdentifier(doc.getUri()));
}
private SpringBootApp[] getRunningSpringApps(IJavaProject project) {
try {
Collection<SpringBootApp> allApps = this.runningSpringBootApps;
if (allApps == null) {
allApps = runningAppProvider.getAllRunningSpringApps();
}
Collection<SpringBootApp> allMatchingApps = RunningAppMatcher.getAllMatchingApps(allApps, project);
return allMatchingApps.toArray(new SpringBootApp[0]);
} catch (Exception e) {
logger.error("error getting all matching projects for project'" + project.getElementName() + "'", e);
return new SpringBootApp[0];
}
}
public void setRunningSpringApps(Collection<SpringBootApp> allRunningSpringBootApps) {
this.runningSpringBootApps = allRunningSpringBootApps;
}
}

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
@@ -20,6 +20,7 @@ import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
import org.eclipse.jdt.core.dom.TypeDeclaration;
import org.eclipse.lsp4j.CodeLens;
import org.eclipse.lsp4j.Hover;
import org.springframework.ide.vscode.boot.java.livehover.v2.SpringProcessLiveData;
import org.springframework.ide.vscode.commons.boot.app.cli.SpringBootApp;
import org.springframework.ide.vscode.commons.java.IJavaProject;
import org.springframework.ide.vscode.commons.util.text.TextDocument;
@@ -29,31 +30,31 @@ import org.springframework.ide.vscode.commons.util.text.TextDocument;
*/
public interface HoverProvider {
default Hover provideHover(ASTNode node, Annotation annotation, ITypeBinding type, int offset, TextDocument doc, IJavaProject project, SpringBootApp[] runningApps) {
default Hover provideHover(ASTNode node, Annotation annotation, ITypeBinding type, int offset, TextDocument doc, IJavaProject project, SpringProcessLiveData[] processLiveData) {
return null;
}
default Hover provideHover(ASTNode node, TypeDeclaration typeDeclaration, ITypeBinding type, int offset, TextDocument doc, IJavaProject project, SpringBootApp[] runningApps) {
default Hover provideHover(ASTNode node, TypeDeclaration typeDeclaration, ITypeBinding type, int offset, TextDocument doc, IJavaProject project, SpringProcessLiveData[] processLiveData) {
return null;
}
default Hover provideHover(MethodDeclaration methodDeclaration, int offset, TextDocument doc, IJavaProject project, SpringBootApp[] runningApps) {
default Hover provideHover(MethodDeclaration methodDeclaration, int offset, TextDocument doc, IJavaProject project, SpringProcessLiveData[] processLiveData) {
return null;
}
default Hover provideMethodParameterHover(SingleVariableDeclaration parameter, int offset, TextDocument doc, IJavaProject project, SpringBootApp[] runningApps) {
default Hover provideMethodParameterHover(SingleVariableDeclaration parameter, int offset, TextDocument doc, IJavaProject project, SpringProcessLiveData[] processLiveData) {
return null;
}
default Collection<CodeLens> getLiveHintCodeLenses(IJavaProject project, Annotation annotation, TextDocument doc, SpringBootApp[] runningApps) {
default Collection<CodeLens> getLiveHintCodeLenses(IJavaProject project, Annotation annotation, TextDocument doc, SpringProcessLiveData[] processLiveData) {
return null;
}
default Collection<CodeLens> getLiveHintCodeLenses(IJavaProject project,TypeDeclaration typeDeclaration, TextDocument doc, SpringBootApp[] runningApps) {
default Collection<CodeLens> getLiveHintCodeLenses(IJavaProject project,TypeDeclaration typeDeclaration, TextDocument doc, SpringProcessLiveData[] processLiveData) {
return null;
}
default Collection<CodeLens> getLiveHintCodeLenses(IJavaProject project, MethodDeclaration methodDeclaration, TextDocument doc, SpringBootApp[] runningApps) {
default Collection<CodeLens> getLiveHintCodeLenses(IJavaProject project, MethodDeclaration methodDeclaration, TextDocument doc, SpringProcessLiveData[] processLiveData) {
return null;
}

View File

@@ -143,7 +143,7 @@ public class RemoteRunningAppsProvider implements RunningAppProvider {
private Map<RemoteBootAppData,SpringBootApp> remoteAppInstances = new HashMap<>();
public RemoteRunningAppsProvider(SimpleLanguageServer server) {
server.getWorkspaceService().onDidChangeConfiguraton(this::handleSettings);
// server.getWorkspaceService().onDidChangeConfiguraton(this::handleSettings);
}
@Override
@@ -152,34 +152,34 @@ public class RemoteRunningAppsProvider implements RunningAppProvider {
}
synchronized void handleSettings(Settings settings) {
RemoteBootAppData[] appData = settings.getAs(RemoteBootAppData[].class, "boot-java", "remote-apps");
if (appData==null) {
//Avoid NPE
appData = new RemoteBootAppData[0];
}
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 (RemoteBootAppData key : newAppData) {
remoteAppInstances.computeIfAbsent(key, (_key) -> {
logger.info("Creating RemoteStringBootApp: "+_key);
return RemoteSpringBootApp.create(key.getJmxurl(), key.getHost(), key.getPort(), key.getUrlScheme(), key.isKeepChecking());
});
}
}
// RemoteBootAppData[] appData = settings.getAs(RemoteBootAppData[].class, "boot-java", "remote-apps");
// if (appData==null) {
// //Avoid NPE
// appData = new RemoteBootAppData[0];
// }
//
// 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 (RemoteBootAppData key : newAppData) {
// remoteAppInstances.computeIfAbsent(key, (_key) -> {
// logger.info("Creating RemoteStringBootApp: "+_key);
// return RemoteSpringBootApp.create(key.getJmxurl(), key.getHost(), key.getPort(), key.getUrlScheme(), key.isKeepChecking());
// });
// }
// }
}
}

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
@@ -29,10 +29,10 @@ import org.slf4j.LoggerFactory;
import org.springframework.ide.vscode.boot.java.autowired.AutowiredHoverProvider;
import org.springframework.ide.vscode.boot.java.handlers.HoverProvider;
import org.springframework.ide.vscode.boot.java.links.SourceLinks;
import org.springframework.ide.vscode.boot.java.livehover.v2.LiveBean;
import org.springframework.ide.vscode.boot.java.livehover.v2.LiveBeansModel;
import org.springframework.ide.vscode.boot.java.livehover.v2.SpringProcessLiveData;
import org.springframework.ide.vscode.boot.java.utils.ASTUtils;
import org.springframework.ide.vscode.commons.boot.app.cli.SpringBootApp;
import org.springframework.ide.vscode.commons.boot.app.cli.livebean.LiveBean;
import org.springframework.ide.vscode.commons.boot.app.cli.livebean.LiveBeansModel;
import org.springframework.ide.vscode.commons.java.IJavaProject;
import org.springframework.ide.vscode.commons.util.text.TextDocument;
@@ -56,21 +56,21 @@ public abstract class AbstractInjectedIntoHoverProvider implements HoverProvider
@FunctionalInterface
protected interface DefinedBeanProvider {
LiveBean definedBean(SpringBootApp app);
LiveBean definedBean(SpringProcessLiveData liveData);
}
@Override
public Collection<CodeLens> getLiveHintCodeLenses(IJavaProject project, Annotation annotation, TextDocument doc,
SpringBootApp[] runningApps) {
SpringProcessLiveData[] processLiveData) {
// Highlight if any running app contains an instance of this component
try {
if (runningApps.length > 0) {
if (processLiveData.length > 0) {
LiveBean definedBean = getDefinedBean(annotation);
if (definedBean != null) {
if (Stream.of(runningApps).anyMatch(app -> LiveHoverUtils.hasRelevantBeans(app, definedBean))) {
if (Stream.of(processLiveData).anyMatch(app -> LiveHoverUtils.hasRelevantBeans(app, definedBean))) {
Optional<Range> nameRange = ASTUtils.nameRange(doc, annotation);
if (nameRange.isPresent()) {
List<CodeLens> codeLenses = assembleCodeLenses(project, runningApps, app -> definedBean, doc, nameRange.get(), annotation);
List<CodeLens> codeLenses = assembleCodeLenses(project, processLiveData, app -> definedBean, doc, nameRange.get(), annotation);
return codeLenses;
}
}
@@ -84,12 +84,12 @@ public abstract class AbstractInjectedIntoHoverProvider implements HoverProvider
@Override
public Hover provideHover(ASTNode node, Annotation annotation, ITypeBinding type, int offset, TextDocument doc,
IJavaProject project, SpringBootApp[] runningApps) {
if (runningApps.length > 0) {
IJavaProject project, SpringProcessLiveData[] processLiveData) {
if (processLiveData.length > 0) {
LiveBean definedBean = getDefinedBean(annotation);
if (definedBean != null) {
Hover hover = assembleHover(project, runningApps, app -> definedBean, annotation, true, true);
Hover hover = assembleHover(project, processLiveData, app -> definedBean, annotation, true, true);
if (hover != null) {
Optional<Range> nameRange = ASTUtils.nameRange(doc, annotation);
if (nameRange.isPresent()) {
@@ -102,22 +102,22 @@ public abstract class AbstractInjectedIntoHoverProvider implements HoverProvider
return null;
}
protected List<CodeLens> assembleCodeLenses(IJavaProject project, SpringBootApp[] runningApps, DefinedBeanProvider definedBeanProvider,
protected List<CodeLens> assembleCodeLenses(IJavaProject project, SpringProcessLiveData[] processLiveData, DefinedBeanProvider definedBeanProvider,
TextDocument doc, Range range, ASTNode node) {
boolean beanFound = false;
for (SpringBootApp app : runningApps) {
for (SpringProcessLiveData liveData : processLiveData) {
LiveBean definedBean = definedBeanProvider.definedBean(app);
LiveBean definedBean = definedBeanProvider.definedBean(liveData);
if (definedBean == null) {
continue;
}
beanFound = true;
List<LiveBean> relevantBeans = LiveHoverUtils.findRelevantBeans(app, definedBean);
List<LiveBean> relevantBeans = LiveHoverUtils.findRelevantBeans(liveData, definedBean);
if (!relevantBeans.isEmpty()) {
List<LiveBean> injectedBeans = getRelevantInjectedIntoBeans(project, app, definedBean, relevantBeans);
List<LiveBean> injectedBeans = getRelevantInjectedIntoBeans(project, liveData, definedBean, relevantBeans);
ImmutableList.Builder<CodeLens> builder = ImmutableList.builder();
if (!injectedBeans.isEmpty()) {
// Break out of the loop. Just look for the first app with injected into beans
@@ -128,8 +128,8 @@ public abstract class AbstractInjectedIntoHoverProvider implements HoverProvider
}
// Wired beans code lenses
List<LiveBean> wiredBeans = findWiredBeans(project, app, relevantBeans, node);
builder.addAll(assembleCodeLenseForAutowired(wiredBeans, project, app, doc, range, node));
List<LiveBean> wiredBeans = findWiredBeans(project, liveData, relevantBeans, node);
builder.addAll(assembleCodeLenseForAutowired(wiredBeans, project, liveData, doc, range, node));
List<CodeLens> codeLenses = builder.build();
return codeLenses.isEmpty() ? ImmutableList.of(new CodeLens(range)) : codeLenses;
@@ -139,28 +139,28 @@ public abstract class AbstractInjectedIntoHoverProvider implements HoverProvider
return beanFound ? ImmutableList.of(new CodeLens(range)) : null;
}
protected List<CodeLens> assembleCodeLenseForAutowired(List<LiveBean> wiredBeans, IJavaProject project, SpringBootApp app, TextDocument doc, Range nameRange, ASTNode astNode) {
protected List<CodeLens> assembleCodeLenseForAutowired(List<LiveBean> wiredBeans, IJavaProject project, SpringProcessLiveData processLiveData, TextDocument doc, Range nameRange, ASTNode astNode) {
return LiveHoverUtils.createCodeLensesForBeans(nameRange, wiredBeans,
AutowiredHoverProvider.BEANS_PREFIX_PLAIN_TEXT, MAX_INLINE_BEANS_STRING_LENGTH,
INLINE_BEANS_STRING_SEPARATOR);
}
protected List<LiveBean> findWiredBeans(IJavaProject project, SpringBootApp app, List<LiveBean> relevantBeans, ASTNode astNode) {
protected List<LiveBean> findWiredBeans(IJavaProject project, SpringProcessLiveData liveData, List<LiveBean> relevantBeans, ASTNode astNode) {
return Collections.emptyList();
}
protected Hover assembleHover(IJavaProject project, SpringBootApp[] runningApps, DefinedBeanProvider definedBeanProvider, ASTNode astNode, boolean injected, boolean wired) {
protected Hover assembleHover(IJavaProject project, SpringProcessLiveData[] processLiveData, DefinedBeanProvider definedBeanProvider, ASTNode astNode, boolean injected, boolean wired) {
StringBuilder hover = new StringBuilder();
for (SpringBootApp app : runningApps) {
for (SpringProcessLiveData liveData : processLiveData) {
LiveBean definedBean = definedBeanProvider.definedBean(app);
LiveBean definedBean = definedBeanProvider.definedBean(liveData);
if (definedBean == null) {
continue;
}
List<LiveBean> relevantBeans = LiveHoverUtils.findRelevantBeans(app, definedBean);
List<LiveBean> relevantBeans = LiveHoverUtils.findRelevantBeans(liveData, definedBean);
if (!relevantBeans.isEmpty()) {
if (hover.length() > 0) {
@@ -168,7 +168,7 @@ public abstract class AbstractInjectedIntoHoverProvider implements HoverProvider
}
if (injected) {
List<LiveBean> injectedBeans = getRelevantInjectedIntoBeans(project, app, definedBean, relevantBeans);
List<LiveBean> injectedBeans = getRelevantInjectedIntoBeans(project, liveData, definedBean, relevantBeans);
if (!injectedBeans.isEmpty()) {
hover.append("**");
hover.append(LiveHoverUtils.createBeansTitleMarkdown(sourceLinks, project, injectedBeans, BEANS_PREFIX_MARKDOWN, MAX_INLINE_BEANS_STRING_LENGTH, INLINE_BEANS_STRING_SEPARATOR));
@@ -181,7 +181,7 @@ public abstract class AbstractInjectedIntoHoverProvider implements HoverProvider
}
if (wired) {
List<LiveBean> wiredBeans = findWiredBeans(project, app, relevantBeans, astNode);
List<LiveBean> wiredBeans = findWiredBeans(project, liveData, relevantBeans, astNode);
if (!wiredBeans.isEmpty()) {
AutowiredHoverProvider.createHoverContentForBeans(sourceLinks, project, hover, wiredBeans);
}
@@ -189,7 +189,7 @@ public abstract class AbstractInjectedIntoHoverProvider implements HoverProvider
hover.append("Bean id: `");
hover.append(definedBean.getId());
hover.append("` \n");
hover.append(LiveHoverUtils.niceAppName(app));
hover.append(LiveHoverUtils.niceAppName(liveData));
}
}
@@ -202,8 +202,8 @@ public abstract class AbstractInjectedIntoHoverProvider implements HoverProvider
}
protected List<LiveBean> getRelevantInjectedIntoBeans(IJavaProject project, SpringBootApp app, LiveBean definedBean, List<LiveBean> relevantBeans) {
LiveBeansModel beans = app.getBeans();
protected List<LiveBean> getRelevantInjectedIntoBeans(IJavaProject project, SpringProcessLiveData processLiveData, LiveBean definedBean, List<LiveBean> relevantBeans) {
LiveBeansModel beans = processLiveData.getBeans();
if (relevantBeans != null) {
return relevantBeans.stream()
.flatMap(b -> beans.getBeansDependingOn(b.getId()).stream())

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
@@ -12,8 +12,8 @@ package org.springframework.ide.vscode.boot.java.livehover;
import static org.springframework.ide.vscode.boot.java.utils.ASTUtils.nameRange;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.Set;
@@ -29,8 +29,8 @@ import org.eclipse.lsp4j.jsonrpc.messages.Either;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ide.vscode.boot.java.handlers.HoverProvider;
import org.springframework.ide.vscode.boot.java.livehover.v2.SpringProcessLiveData;
import org.springframework.ide.vscode.boot.java.utils.ASTUtils;
import org.springframework.ide.vscode.commons.boot.app.cli.SpringBootApp;
import org.springframework.ide.vscode.commons.java.IJavaProject;
import org.springframework.ide.vscode.commons.util.text.TextDocument;
@@ -46,27 +46,23 @@ public class ActiveProfilesProvider implements HoverProvider {
private static final Logger log = LoggerFactory.getLogger(ActiveProfilesProvider.class);
@Override
public Hover provideHover(
ASTNode node,
Annotation annotation,
ITypeBinding type,
int offset,
TextDocument doc, IJavaProject project, SpringBootApp[] runningApps
) {
if (runningApps.length > 0) {
public Hover provideHover(ASTNode node, Annotation annotation, ITypeBinding type, int offset,
TextDocument doc, IJavaProject project, SpringProcessLiveData[] processLiveData) {
if (processLiveData.length > 0) {
StringBuilder markdown = new StringBuilder();
markdown.append("**Active Profiles**\n\n");
boolean hasInterestingApp = false;
for (SpringBootApp app : runningApps) {
List<String> profiles = app.getActiveProfiles();
if (profiles==null) {
markdown.append(LiveHoverUtils.niceAppName(app)+" : _Unknown_\n\n");
for (SpringProcessLiveData liveData : processLiveData) {
String[] profiles = liveData.getActiveProfiles();
if (profiles == null) {
markdown.append(LiveHoverUtils.niceAppName(liveData)+" : _Unknown_\n\n");
} else {
hasInterestingApp = true;
if (profiles.isEmpty()) {
markdown.append(LiveHoverUtils.niceAppName(app)+" : _None_\n\n");
if (profiles.length == 0) {
markdown.append(LiveHoverUtils.niceAppName(liveData)+" : _None_\n\n");
} else {
markdown.append(LiveHoverUtils.niceAppName(app)+" :\n");
markdown.append(LiveHoverUtils.niceAppName(liveData)+" :\n");
for (String profile : profiles) {
markdown.append("- "+profile+"\n");
}
@@ -91,11 +87,11 @@ public class ActiveProfilesProvider implements HoverProvider {
}
@Override
public Collection<CodeLens> getLiveHintCodeLenses(IJavaProject project, Annotation annotation, TextDocument doc, SpringBootApp[] runningApps) {
if (runningApps.length > 0) {
public Collection<CodeLens> getLiveHintCodeLenses(IJavaProject project, Annotation annotation, TextDocument doc, SpringProcessLiveData[] processLiveData) {
if (processLiveData.length > 0) {
Builder<CodeLens> codeLenses = ImmutableList.builder();
Set<String> allActiveProfiles = getAllActiveProfiles(runningApps);
Set<String> allActiveProfiles = getAllActiveProfiles(processLiveData);
if (allActiveProfiles != null && allActiveProfiles.size() > 0) {
nameRange(doc, annotation).map(CodeLens::new).ifPresent(codeLenses::add);
}
@@ -115,12 +111,12 @@ public class ActiveProfilesProvider implements HoverProvider {
return ImmutableList.of();
}
private static Set<String> getAllActiveProfiles(SpringBootApp[] runningApps) {
private static Set<String> getAllActiveProfiles(SpringProcessLiveData[] processLiveData) {
ImmutableSet.Builder<String> builder = ImmutableSet.builder();
for (SpringBootApp app : runningApps) {
List<String> profiles = app.getActiveProfiles();
if (profiles!=null) {
builder.addAll(app.getActiveProfiles());
for (SpringProcessLiveData liveData : processLiveData) {
String[] profiles = liveData.getActiveProfiles();
if (profiles != null) {
builder.addAll(Arrays.asList(liveData.getActiveProfiles()));
}
}
return builder.build();

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
@@ -26,9 +26,9 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ide.vscode.boot.java.autowired.AutowiredHoverProvider;
import org.springframework.ide.vscode.boot.java.links.SourceLinks;
import org.springframework.ide.vscode.boot.java.livehover.v2.LiveBean;
import org.springframework.ide.vscode.boot.java.livehover.v2.SpringProcessLiveData;
import org.springframework.ide.vscode.boot.java.utils.ASTUtils;
import org.springframework.ide.vscode.commons.boot.app.cli.SpringBootApp;
import org.springframework.ide.vscode.commons.boot.app.cli.livebean.LiveBean;
import org.springframework.ide.vscode.commons.java.IJavaProject;
import org.springframework.ide.vscode.commons.util.Optionals;
import org.springframework.ide.vscode.commons.util.text.TextDocument;
@@ -83,33 +83,33 @@ public class BeanInjectedIntoHoverProvider extends AbstractInjectedIntoHoverProv
}
@Override
protected List<LiveBean> findWiredBeans(IJavaProject project, SpringBootApp app, List<LiveBean> relevantBeans, ASTNode astNode) {
protected List<LiveBean> findWiredBeans(IJavaProject project, SpringProcessLiveData liveData, List<LiveBean> relevantBeans, ASTNode astNode) {
if (astNode instanceof Annotation) {
// @Bean annotation case
MethodDeclaration beanMethod = ASTUtils.getAnnotatedMethod((Annotation) astNode);
if (beanMethod != null) {
return AutowiredHoverProvider.getRelevantAutowiredBeans(project, beanMethod, app, relevantBeans);
return AutowiredHoverProvider.getRelevantAutowiredBeans(project, beanMethod, liveData, relevantBeans);
}
} else if (astNode instanceof SingleVariableDeclaration) {
// Bean method parameter case
return AutowiredHoverProvider.getRelevantAutowiredBeans(project, astNode, app, relevantBeans);
return AutowiredHoverProvider.getRelevantAutowiredBeans(project, astNode, liveData, relevantBeans);
}
return Collections.emptyList();
}
@Override
protected List<CodeLens> assembleCodeLenseForAutowired(List<LiveBean> wiredBeans, IJavaProject project,
SpringBootApp app, TextDocument doc, Range nameRange, ASTNode astNode) {
SpringProcessLiveData liveData, TextDocument doc, Range nameRange, ASTNode astNode) {
ImmutableList.Builder<CodeLens> builder = ImmutableList.builder();
// Code lens for the @Bean annotation
builder.addAll(super.assembleCodeLenseForAutowired(wiredBeans, project, app, doc, nameRange, astNode));
builder.addAll(super.assembleCodeLenseForAutowired(wiredBeans, project, liveData, doc, nameRange, astNode));
if (astNode instanceof Annotation) {
// Add code lenses for method parameters
MethodDeclaration beanMethod = ASTUtils.getAnnotatedMethod((Annotation) astNode);
if (beanMethod != null) {
builder.addAll(LiveHoverUtils.createCodeLensForMethodParameters(app, project, beanMethod, doc, wiredBeans));
builder.addAll(LiveHoverUtils.createCodeLensForMethodParameters(liveData, project, beanMethod, doc, wiredBeans));
}
}
@@ -118,16 +118,16 @@ public class BeanInjectedIntoHoverProvider extends AbstractInjectedIntoHoverProv
@Override
public Hover provideMethodParameterHover(SingleVariableDeclaration parameter, int offset, TextDocument doc,
IJavaProject project, SpringBootApp[] runningApps) {
IJavaProject project, SpringProcessLiveData[] processLiveData) {
try {
if (runningApps.length > 0) {
if (processLiveData.length > 0) {
Range range = ASTUtils.nodeRegion(doc, parameter.getName()).asRange();
MethodDeclaration method = (MethodDeclaration) parameter.getParent();
Annotation beanAnnotation = ASTUtils.getBeanAnnotation(method);
if (beanAnnotation != null) {
LiveBean definedBean = getDefinedBean(beanAnnotation);
if (definedBean != null) {
Hover hover = assembleHover(project, runningApps, app -> definedBean, parameter, false, true);
Hover hover = assembleHover(project, processLiveData, app -> definedBean, parameter, false, true);
if (hover != null) {
hover.setRange(range);
}

View File

@@ -31,10 +31,10 @@ import org.springframework.ide.vscode.boot.java.Annotations;
import org.springframework.ide.vscode.boot.java.annotations.AnnotationHierarchies;
import org.springframework.ide.vscode.boot.java.beans.BeanUtils;
import org.springframework.ide.vscode.boot.java.links.SourceLinks;
import org.springframework.ide.vscode.boot.java.livehover.v2.LiveBean;
import org.springframework.ide.vscode.boot.java.livehover.v2.LiveBeansModel;
import org.springframework.ide.vscode.boot.java.livehover.v2.SpringProcessLiveData;
import org.springframework.ide.vscode.boot.java.utils.ASTUtils;
import org.springframework.ide.vscode.commons.boot.app.cli.SpringBootApp;
import org.springframework.ide.vscode.commons.boot.app.cli.livebean.LiveBean;
import org.springframework.ide.vscode.commons.boot.app.cli.livebean.LiveBeansModel;
import org.springframework.ide.vscode.commons.java.IJavaProject;
import org.springframework.ide.vscode.commons.util.BadLocationException;
import org.springframework.ide.vscode.commons.util.StringUtil;
@@ -103,15 +103,15 @@ public class ComponentInjectionsHoverProvider extends AbstractInjectedIntoHoverP
@Override
public Collection<CodeLens> getLiveHintCodeLenses(IJavaProject project, TypeDeclaration typeDeclaration,
TextDocument doc, SpringBootApp[] runningApps) {
if (runningApps.length > 0 && !isComponentAnnotatedType(typeDeclaration)) {
TextDocument doc, SpringProcessLiveData[] processLiveData) {
if (processLiveData.length > 0 && !isComponentAnnotatedType(typeDeclaration)) {
try {
ITypeBinding beanType = typeDeclaration.resolveBinding();
if (beanType != null) {
String id = getBeanId(null, beanType, Flags.isStatic(typeDeclaration.getModifiers()));
Optional<Range> nameRange = Optional.of(ASTUtils.nodeRegion(doc, typeDeclaration.getName()).asRange());
if (nameRange.isPresent()) {
List<CodeLens> codeLenses = assembleCodeLenses(project, runningApps, app -> definedBean(app, getBeanType(beanType), id), doc,
List<CodeLens> codeLenses = assembleCodeLenses(project, processLiveData, liveData -> definedBean(liveData, getBeanType(beanType), id), doc,
nameRange.get(), typeDeclaration);
if (codeLenses != null) {
return codeLenses.isEmpty() ? ImmutableList.of(new CodeLens(nameRange.get())) : codeLenses;
@@ -125,8 +125,8 @@ public class ComponentInjectionsHoverProvider extends AbstractInjectedIntoHoverP
return ImmutableList.of();
}
private LiveBean definedBean(SpringBootApp app, String beanType, String possibleId) {
LiveBeansModel beans = app.getBeans();
private LiveBean definedBean(SpringProcessLiveData liveData, String beanType, String possibleId) {
LiveBeansModel beans = liveData.getBeans();
if (beans != null) {
if (beans.getBeansOfName(possibleId).isEmpty()) {
// try bean type if there is only one bean of such type
@@ -144,14 +144,14 @@ public class ComponentInjectionsHoverProvider extends AbstractInjectedIntoHoverP
@Override
public Hover provideHover(ASTNode node, TypeDeclaration typeDeclaration, ITypeBinding type, int offset,
TextDocument doc, IJavaProject project, SpringBootApp[] runningApps) {
TextDocument doc, IJavaProject project, SpringProcessLiveData[] processLiveData) {
if (runningApps.length > 0 && !isComponentAnnotatedType(typeDeclaration)) {
if (processLiveData.length > 0 && !isComponentAnnotatedType(typeDeclaration)) {
ITypeBinding beanType = typeDeclaration.resolveBinding();
if (beanType != null) {
String id = getBeanId(null, beanType, Flags.isStatic(typeDeclaration.getModifiers()));
Hover hover = assembleHover(project, runningApps, app -> definedBean(app, getBeanType(beanType), id), typeDeclaration, true, true);
Hover hover = assembleHover(project, processLiveData, app -> definedBean(app, getBeanType(beanType), id), typeDeclaration, true, true);
if (hover != null) {
SimpleName name = typeDeclaration.getName();
try {
@@ -167,7 +167,7 @@ public class ComponentInjectionsHoverProvider extends AbstractInjectedIntoHoverP
}
@Override
protected List<LiveBean> findWiredBeans(IJavaProject project, SpringBootApp app, List<LiveBean> relevantBeans,
protected List<LiveBean> findWiredBeans(IJavaProject project, SpringProcessLiveData liveData, List<LiveBean> relevantBeans,
ASTNode astNode) {
TypeDeclaration typeDeclaration = null;
if (astNode instanceof TypeDeclaration) {
@@ -175,7 +175,7 @@ public class ComponentInjectionsHoverProvider extends AbstractInjectedIntoHoverP
} else if (astNode instanceof Annotation) {
typeDeclaration = ASTUtils.getAnnotatedType((Annotation) astNode);
}
return typeDeclaration == null ? Collections.emptyList() : LiveHoverUtils.findAllDependencyBeans(app, relevantBeans);
return typeDeclaration == null ? Collections.emptyList() : LiveHoverUtils.findAllDependencyBeans(liveData, relevantBeans);
}
private boolean isComponentAnnotatedType(TypeDeclaration typeDeclaration) {

View File

@@ -26,11 +26,11 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ide.vscode.boot.java.autowired.AutowiredHoverProvider;
import org.springframework.ide.vscode.boot.java.links.SourceLinks;
import org.springframework.ide.vscode.boot.java.livehover.v2.LiveBean;
import org.springframework.ide.vscode.boot.java.livehover.v2.LiveBeansModel;
import org.springframework.ide.vscode.boot.java.livehover.v2.SpringProcessLiveData;
import org.springframework.ide.vscode.boot.java.utils.ASTUtils;
import org.springframework.ide.vscode.boot.java.utils.SpringResource;
import org.springframework.ide.vscode.commons.boot.app.cli.SpringBootApp;
import org.springframework.ide.vscode.commons.boot.app.cli.livebean.LiveBean;
import org.springframework.ide.vscode.commons.boot.app.cli.livebean.LiveBeansModel;
import org.springframework.ide.vscode.commons.java.IJavaProject;
import org.springframework.ide.vscode.commons.util.BadLocationException;
import org.springframework.ide.vscode.commons.util.Renderables;
@@ -137,12 +137,12 @@ public class LiveHoverUtils {
return new SpringResource(sourceLinks, resource, project).toMarkdown();
}
public static boolean hasRelevantBeans(SpringBootApp app, LiveBean definedBean) {
return findRelevantBeans(app, definedBean).stream().findAny().isPresent();
public static boolean hasRelevantBeans(SpringProcessLiveData liveData, LiveBean definedBean) {
return findRelevantBeans(liveData, definedBean).stream().findAny().isPresent();
}
public static List<LiveBean> findRelevantBeans(SpringBootApp app, LiveBean definedBean) {
LiveBeansModel beansModel = app.getBeans();
public static List<LiveBean> findRelevantBeans(SpringProcessLiveData liveData, LiveBean definedBean) {
LiveBeansModel beansModel = liveData.getBeans();
if (beansModel != null) {
List<LiveBean> relevantBeans = beansModel.getBeansOfName(definedBean.getId());
String type = definedBean.getType(true);
@@ -159,20 +159,20 @@ public class LiveHoverUtils {
return Collections.emptyList();
}
public static List<LiveBean> findAllDependencyBeans(SpringBootApp app, List<LiveBean> relevantBeans) {
LiveBeansModel beans = app.getBeans();
public static List<LiveBean> findAllDependencyBeans(SpringProcessLiveData liveData, List<LiveBean> relevantBeans) {
LiveBeansModel beans = liveData.getBeans();
return relevantBeans.stream()
.flatMap(b -> Arrays.stream(b.getDependencies())).distinct()
.flatMap(d -> beans.getBeansOfName(d).stream()).collect(Collectors.toList());
}
public static String niceAppName(SpringBootApp app) {
public static String niceAppName(SpringProcessLiveData liveData) {
try {
return niceAppName(app.getProcessID(), app.getProcessName());
return niceAppName(liveData.getProcessID(), liveData.getProcessName());
} catch (Exception e) {
log.error("", e);
return app.toString();
return liveData.toString();
}
}
@@ -200,7 +200,7 @@ public class LiveHoverUtils {
}
@SuppressWarnings("unchecked")
public static List<CodeLens> createCodeLensForMethodParameters(SpringBootApp app, IJavaProject project, MethodDeclaration method, TextDocument doc, List<LiveBean> wiredBeans) {
public static List<CodeLens> createCodeLensForMethodParameters(SpringProcessLiveData liveData, IJavaProject project, MethodDeclaration method, TextDocument doc, List<LiveBean> wiredBeans) {
ImmutableList.Builder<CodeLens> builder = ImmutableList.builder();
method.parameters().forEach(p -> {
if (p instanceof SingleVariableDeclaration) {

View File

@@ -8,7 +8,7 @@
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.commons.boot.app.cli.livebean;
package org.springframework.ide.vscode.boot.java.livehover.v2;
import java.util.Arrays;

View File

@@ -8,7 +8,7 @@
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.commons.boot.app.cli.livebean;
package org.springframework.ide.vscode.boot.java.livehover.v2;
import java.util.ArrayList;
import java.util.List;
@@ -16,7 +16,8 @@ import java.util.Set;
import org.json.JSONArray;
import org.json.JSONObject;
import org.springframework.ide.vscode.commons.util.Log;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ide.vscode.commons.util.StringUtil;
import com.google.common.collect.ImmutableListMultimap;
@@ -27,6 +28,8 @@ import com.google.common.collect.ImmutableListMultimap;
*/
public class LiveBeansModel {
private static final Logger log = LoggerFactory.getLogger(LiveBeansModel.class);
private static final String[] NO_STRINGS = new String[] {};
public static class Builder {
@@ -196,7 +199,7 @@ public class LiveBeansModel {
//Only getting here if none of the parsers worked... So if at least one parser works,
// we won't log any exceptions.
for (Exception e : exceptions) {
Log.log(e);
log.warn(e.getMessage());
}
return LiveBeansModel.builder().build(); // always return at least an empty model.
}

View File

@@ -8,7 +8,7 @@
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.commons.boot.app.cli;
package org.springframework.ide.vscode.boot.java.livehover.v2;
public class LiveConditional {

View File

@@ -8,7 +8,7 @@
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.commons.boot.app.cli;
package org.springframework.ide.vscode.boot.java.livehover.v2;
import java.util.ArrayList;
import java.util.List;
@@ -16,7 +16,8 @@ import java.util.Optional;
import org.json.JSONArray;
import org.json.JSONObject;
import org.springframework.ide.vscode.commons.util.Log;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ide.vscode.commons.util.StringUtil;
/**
@@ -30,6 +31,8 @@ import org.springframework.ide.vscode.commons.util.StringUtil;
*
*/
public class LiveConditionalParser {
private static final Logger log = LoggerFactory.getLogger(LiveConditionalParser.class);
private final String autoConfigRecord;
private final String appProcessName;
@@ -41,7 +44,7 @@ public class LiveConditionalParser {
this.appProcessName = appProcessName;
}
public Optional<List<LiveConditional>> parse() {
public LiveConditional[] parse() {
try {
List<LiveConditional> allConditionals = new ArrayList<>();
if (StringUtil.hasText(autoConfigRecord)) {
@@ -58,12 +61,12 @@ public class LiveConditionalParser {
}
}
if (!allConditionals.isEmpty()) {
return Optional.of(allConditionals);
return (LiveConditional[]) allConditionals.toArray(new LiveConditional[allConditionals.size()]);
}
} catch (Exception e) {
Log.log(e);
log.warn(e.getMessage());
}
return Optional.empty();
return new LiveConditional[0];
}
/**
@@ -152,7 +155,7 @@ public class LiveConditionalParser {
}
public static Optional<List<LiveConditional>> parse(String autoConfigRecord, String appProcessId,
public static LiveConditional[] parse(String autoConfigRecord, String appProcessId,
String appProcessName) {
return new LiveConditionalParser(autoConfigRecord, appProcessId, appProcessName).parse();
}

View File

@@ -8,7 +8,7 @@
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.commons.boot.app.cli.liveproperties;
package org.springframework.ide.vscode.boot.java.livehover.v2;
import java.util.ArrayList;
import java.util.List;
@@ -23,7 +23,6 @@ public class LiveProperties {
this.sources = sources != null ? ImmutableList.copyOf(sources) : ImmutableList.of();
}
public List<LiveProperty> getProperties(String propertyName) {
List<LiveProperty> foundProperties = new ArrayList<>();
for (LivePropertySource source : sources) {

View File

@@ -8,7 +8,7 @@
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.commons.boot.app.cli.liveproperties;
package org.springframework.ide.vscode.boot.java.livehover.v2;
import java.util.List;

View File

@@ -8,7 +8,7 @@
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.commons.boot.app.cli.liveproperties;
package org.springframework.ide.vscode.boot.java.livehover.v2;
import java.util.Iterator;
import java.util.List;

View File

@@ -8,7 +8,7 @@
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.commons.boot.app.cli.liveproperties;
package org.springframework.ide.vscode.boot.java.livehover.v2;
import java.util.ArrayList;
import java.util.Iterator;

View File

@@ -8,7 +8,7 @@
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.commons.boot.app.cli.liveproperties;
package org.springframework.ide.vscode.boot.java.livehover.v2;
public class LiveProperty {

View File

@@ -8,7 +8,7 @@
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.commons.boot.app.cli.liveproperties;
package org.springframework.ide.vscode.boot.java.livehover.v2;
import java.util.List;
@@ -19,7 +19,6 @@ 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();

View File

@@ -8,11 +8,11 @@
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.commons.boot.app.cli.requestmappings;
package org.springframework.ide.vscode.boot.java.livehover.v2;
import java.util.Set;
public interface RequestMapping {
public interface LiveRequestMapping {
// String getPath(); commented... because... not used??
String[] getSplitPath();
String getFullyQualifiedClassName();

View File

@@ -8,7 +8,7 @@
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.commons.boot.app.cli.requestmappings;
package org.springframework.ide.vscode.boot.java.livehover.v2;
import java.util.Arrays;
import java.util.Collections;
@@ -23,7 +23,7 @@ import org.springframework.ide.vscode.commons.java.parser.JLRMethodParser.JLRMet
import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
public abstract class AbstractRequestMapping implements RequestMapping {
public abstract class LiveRequestMappingAbstractImpl implements LiveRequestMapping {
private static final Pattern REQUEST_METHODS_PATTERN = Pattern.compile(".*methods=\\[(.*)\\].*");
@@ -31,7 +31,7 @@ public abstract class AbstractRequestMapping implements RequestMapping {
final private Supplier<Set<String>> requestMethodsSupplier;
final private Supplier<String[]> pathsSuplier;
public AbstractRequestMapping() {
public LiveRequestMappingAbstractImpl() {
this.requestMethodsSupplier = Suppliers.memoize(() -> parseRequestMethods());
this.methodDataSupplier = Suppliers.memoize(() -> JLRMethodParser.parse(getMethodString()));
this.pathsSuplier = Suppliers.memoize(() -> computePaths());
@@ -90,13 +90,13 @@ public abstract class AbstractRequestMapping implements RequestMapping {
int end = predicate.indexOf(']');
if (end>=2) {
String pathString = predicate.substring(start, end);
return ParseUtil.splitPaths(pathString);
return splitPaths(pathString);
}
}
//Case 1, or some unanticipated stuff.
//Assume the key is the paths strk g, which is right for Case 1
// and probably more useful than null for 'unanticipated stuff'.
return ParseUtil.splitPaths(predicate);
return splitPaths(predicate);
}
protected abstract String getPredicateString();
@@ -106,4 +106,18 @@ public abstract class AbstractRequestMapping implements RequestMapping {
return pathsSuplier.get();
}
protected String[] splitPaths(String paths) {
return Arrays.stream(paths.split("\\|\\|"))
.map(s -> s.trim())
.filter(s -> !s.isEmpty())
.map(s -> {
if (s.charAt(0) != '/') {
return '/' + s;
} else {
return s;
}
})
.toArray(String[]::new);
}
}

View File

@@ -8,13 +8,13 @@
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.commons.boot.app.cli.requestmappings;
package org.springframework.ide.vscode.boot.java.livehover.v2;
import org.json.JSONObject;
import com.google.common.base.Objects;
public class Boot1xRequestMapping extends AbstractRequestMapping {
public class LiveRequestMappingBoot1xRequestMapping extends LiveRequestMappingAbstractImpl {
/*
There are two styles of entries:
@@ -34,7 +34,7 @@ There are two styles of entries:
private JSONObject beanInfo;
private String pathKey;
public Boot1xRequestMapping(String pathKey, JSONObject beanInfo) {
public LiveRequestMappingBoot1xRequestMapping(String pathKey, JSONObject beanInfo) {
this.pathKey = pathKey;
this.beanInfo = beanInfo;
}
@@ -52,7 +52,7 @@ There are two styles of entries:
return false;
if (getClass() != obj.getClass())
return false;
Boot1xRequestMapping other = (Boot1xRequestMapping) obj;
LiveRequestMappingBoot1xRequestMapping other = (LiveRequestMappingBoot1xRequestMapping) obj;
return Objects.equal(this.pathKey, other.pathKey)
&& Objects.equal(this.getMethodString(), other.getMethodString());
}

View File

@@ -8,7 +8,7 @@
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.commons.boot.app.cli.requestmappings;
package org.springframework.ide.vscode.boot.java.livehover.v2;
import java.util.ArrayList;
import java.util.Collections;
@@ -20,7 +20,7 @@ import org.json.JSONArray;
import org.json.JSONObject;
import org.objectweb.asm.Type;
public class Boot20DispatcherServletMapping implements RequestMapping {
public class LiveRequestMappingBoot2xDispatcherServletMapping implements LiveRequestMapping {
/*
@@ -54,7 +54,7 @@ Example entry:
private JSONObject data;
public Boot20DispatcherServletMapping(JSONObject data) {
public LiveRequestMappingBoot2xDispatcherServletMapping(JSONObject data) {
this.data = data;
}

View File

@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2018 Pivotal, Inc.
* Copyright (c) 2018, 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
@@ -8,20 +8,19 @@
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.commons.boot.app.cli.requestmappings;
package org.springframework.ide.vscode.boot.java.livehover.v2;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.json.JSONArray;
import org.json.JSONObject;
public class RequestMappingsParser20 {
public class LiveRequestMappingBoot2xParser {
public static Collection<RequestMapping> parse(JSONObject obj) {
public static LiveRequestMapping[] parse(JSONObject obj) {
obj = obj.getJSONObject("contexts");
List<RequestMapping> result = new ArrayList<>();
List<LiveRequestMapping> result = new ArrayList<>();
for (String contextId : obj.keySet()) {
//Contains 3 different keys now ('dispatcherServlets', 'servletFilters' and 'servlets'.
// Each with their own kind of data inside. Looks like 'dispatcherServlets' contains stuff similar to what we
@@ -40,11 +39,11 @@ public class RequestMappingsParser20 {
if (rmArray != null) {
for (Object e : rmArray) {
JSONObject rm = (JSONObject)e;
result.add(new Boot20DispatcherServletMapping(rm));
result.add(new LiveRequestMappingBoot2xDispatcherServletMapping(rm));
}
}
}
return result;
return (LiveRequestMapping[]) result.toArray(new LiveRequestMapping[result.size()]);
}
}

View File

@@ -0,0 +1,18 @@
/*******************************************************************************
* 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
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.boot.java.livehover.v2;
/**
* @author mlippert
*/
public class SpringProcessCommandHandler {
}

View File

@@ -0,0 +1,22 @@
/*******************************************************************************
* 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
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.boot.java.livehover.v2;
/**
* @author Martin Lippert
*/
public interface SpringProcessConnector {
void connect() throws Exception;
void refresh() throws Exception;
void disconnect() throws Exception;
}

View File

@@ -0,0 +1,215 @@
/*******************************************************************************
* 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
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.boot.java.livehover.v2;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ide.vscode.commons.java.IJavaProject;
import org.springframework.ide.vscode.commons.languageserver.java.ProjectObserver;
import com.sun.tools.attach.VirtualMachine;
import com.sun.tools.attach.VirtualMachineDescriptor;
/**
* @author Martin Lippert
*/
@SuppressWarnings("restriction")
public class SpringProcessConnectorLocal {
private static final Logger log = LoggerFactory.getLogger(SpringProcessConnectorLocal.class);
private static final String LOCAL_CONNECTOR_ADDRESS = "com.sun.management.jmxremote.localConnectorAddress";
/**
* this property is automatically set my language servers to their own processes
* to avoid the spring boot language server to connect to itself or another language server process
*
* this is primarily an optimization to avoid that overhead of trying to connect to such a process
*/
private static final String LANGUAGE_SERVER_PROPERTY = "sts4.languageserver.name";
/**
* this property can be set to a running spring boot app at startup to indicatew that the
* process of this running boot app belongs to a specific project
*
* in that case, the running process is only connected if the workspace contains that project
*/
private static final String SPRING_APP_PROJECT_NAME_PROPERTY = "spring.boot.project.name";
/**
* common prefix for the vm descriptor display name of Eclipse processes, we can filter that out
* of the list of processes to connect to immediately to avoid further processing
*/
private static final String ECLIPSE_PROCESS_DISPLAY_NAME_PREFIX = "org.eclipse.equinox.launcher.Main";
private final Collection<String> projects;
private final Set<String> processes;
private final SpringProcessConnectorService processConnectorService;
private final SpringProcessLiveDataProvider liveDataProvider;
public SpringProcessConnectorLocal(SpringProcessConnectorService processConnector, SpringProcessLiveDataProvider liveDataProvider,
ProjectObserver projectObserver) {
this.projects = Collections.synchronizedCollection(new HashSet<>());
this.processes = Collections.synchronizedSet(new HashSet<>());
this.liveDataProvider = liveDataProvider;
this.processConnectorService = processConnector;
projectObserver.addListener(new ProjectObserver.Listener() {
@Override
public void created(IJavaProject project) {
projects.add(project.getElementName());
}
@Override
public void deleted(IJavaProject project) {
projects.remove(project.getElementName());
}
@Override
public void changed(IJavaProject project) {
}
});
}
public void searchForNewProcesses() {
List<VirtualMachineDescriptor> currentVms = VirtualMachine.list();
Set<String> currentVMKeys = new HashSet<>();
for (VirtualMachineDescriptor vm : currentVms) {
try {
String processID = getProcessID(vm);
String processName = getProcessName(vm);
String processKey = SpringProcessConnectorService.getProcessKey(processID, processName);
currentVMKeys.add(processKey);
if (!processes.contains(processKey)) {
connectLocalProcess(processKey, vm, true);
}
}
catch (Exception e) {
log.error("error looking into local process: " + vm.id(), e);
}
}
Iterator<String> i = processes.iterator();
while (i.hasNext()) {
String processKey = i.next();
if (!currentVMKeys.contains(processKey)) {
i.remove();
processConnectorService.disconnectProcess(processKey);
}
}
}
public void connectLocalProcess(String processKey, VirtualMachineDescriptor vmDescriptor, boolean checkAutoConnect) {
processes.add(processKey);
VirtualMachine vm = null;
try {
String jmxAddress = null;
vm = VirtualMachine.attach(vmDescriptor);
if (checkAutoConnect && !shouldAutoConnectToProcess(vmDescriptor, vm)) {
return;
}
try {
jmxAddress = vm.getAgentProperties().getProperty(LOCAL_CONNECTOR_ADDRESS);
} catch (Exception e) {
//ignore
}
if (jmxAddress == null) {
try {
jmxAddress = vm.startLocalManagementAgent();
} catch (Exception e) {
log.error("Error starting local management agent", e);
}
}
if (jmxAddress != null) {
String processID = getProcessID(vmDescriptor);
String processName = getProcessName(vmDescriptor);
String urlScheme = "http";
SpringProcessConnectorOverJMX connector = new SpringProcessConnectorOverJMX(
liveDataProvider, processKey, jmxAddress, urlScheme, processID, processName, null, null, false);
this.processConnectorService.connectProcess(processKey, connector);
}
}
catch (Exception e) {
log.error("exception while connecting to jvm process", e);
}
finally {
if (vm != null) {
try {
vm.detach();
}
catch (Exception e) {
log.error("error detaching from vm: " + vmDescriptor.id(), e);
}
}
}
}
private boolean shouldAutoConnectToProcess(VirtualMachineDescriptor vmDescriptor, VirtualMachine vm) {
try {
String displayName = vmDescriptor.displayName();
if (displayName != null && displayName.startsWith(ECLIPSE_PROCESS_DISPLAY_NAME_PREFIX)) {
log.info("Eclipse process found, do not connect: " + vmDescriptor.id());
return false;
}
Properties systemProperties = vm.getSystemProperties();
Object projectNameProperty = systemProperties.get(SPRING_APP_PROJECT_NAME_PROPERTY);
if (projectNameProperty instanceof String) {
log.info("Spring boot process found: " + projectNameProperty);
return this.processes.contains((String) projectNameProperty);
}
Object languageServerIndicatorProperty = systemProperties.get(LANGUAGE_SERVER_PROPERTY);
if (languageServerIndicatorProperty != null) {
log.info("language server process found, do not connect: " + vmDescriptor.id());
return false;
}
}
catch (Exception e) {
return false;
}
// default case:
return true;
}
private String getProcessID(VirtualMachineDescriptor descriptor) {
return descriptor.id();
}
private String getProcessName(VirtualMachineDescriptor descriptor) {
String rawName = descriptor.displayName();
int firstSpace = rawName.indexOf(' ');
return firstSpace < 0 ? rawName : rawName.substring(0, firstSpace);
}
}

View File

@@ -0,0 +1,98 @@
/*******************************************************************************
* 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
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.boot.java.livehover.v2;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author Martin Lippert
*/
public class SpringProcessConnectorOverJMX implements SpringProcessConnector {
private static final Logger log = LoggerFactory.getLogger(SpringProcessConnectorOverJMX.class);
private final SpringProcessLiveDataProvider liveDataProvider;
private final String processKey;
private final String jmxURL;
private final String urlScheme;
private final String processID;
private final String processName;
private final String host;
private final String port;
private final boolean keepChecking;
public SpringProcessConnectorOverJMX(SpringProcessLiveDataProvider liveDataProvider, String processKey, String jmxURL,
String urlScheme, String processID, String processName, String host, String port, boolean keepChecking) {
this.liveDataProvider = liveDataProvider;
this.processKey = processKey;
this.jmxURL = jmxURL;
this.urlScheme = urlScheme;
this.processID = processID;
this.processName = processName;
this.host = host;
this.port = port;
this.keepChecking = keepChecking;
}
@Override
public void connect() throws Exception {
}
@Override
public void refresh() throws Exception {
log.info("try to open JMX connection to: " + jmxURL);
JMXConnector jmxConnector = null;
try {
SpringProcessLiveDataExtractorOverJMX springJMXConnector = new SpringProcessLiveDataExtractorOverJMX();
JMXServiceURL jmxServiceURL = new JMXServiceURL(jmxURL);
jmxConnector = JMXConnectorFactory.connect(jmxServiceURL, null);
String hostName = host != null ? host : jmxServiceURL.getHost();
log.info("retrieve live data from: " + jmxURL);
SpringProcessLiveData liveData = springJMXConnector.retrieveLiveData(jmxConnector, processID, processName, urlScheme, hostName, null, port);
if (liveData != null && liveData.getBeans() != null && !liveData.getBeans().isEmpty()) {
this.liveDataProvider.add(processKey, liveData);
return;
}
}
catch (Exception e) {
log.error("exception while connecting to jmx: " + jmxURL, e);
}
finally {
if (jmxConnector != null) {
try {
log.info("close JMX connection to: " + jmxURL);
jmxConnector.close();
}
catch (Exception e) {
log.error("error closing the JMX connection for: " + jmxURL, e);
}
}
}
throw new Exception("no live data received, lets try again");
}
@Override
public void disconnect() throws Exception {
this.liveDataProvider.remove(processKey);
}
}

View File

@@ -0,0 +1,206 @@
/*******************************************************************************
* 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
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.boot.java.livehover.v2;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ide.vscode.commons.languageserver.util.Settings;
import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer;
/**
* @author Martin Lippert
*/
public class SpringProcessConnectorRemote {
public static class RemoteBootAppData {
private String jmxurl;
private String host;
private String urlScheme = "https";
private String port = "443";
private boolean keepChecking = true;
//keepChecking defaults to true. Boot dash automatic remote apps should override this explicitly.
//Reason. All other 'sources' of remote apps are 'manual' and we want them to default to
//'keepChecking' even if the user doesn't set this to true manually.
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;
}
public String getUrlScheme() {
return urlScheme;
}
public void setUrlScheme(String urlScheme) {
this.urlScheme = urlScheme;
}
public String getPort() {
return port;
}
public void setPort(String port) {
this.port = port;
}
public boolean isKeepChecking() {
return keepChecking;
}
public void setKeepChecking(boolean keepChecking) {
this.keepChecking = keepChecking;
}
@Override
public String toString() {
return "RemoteBootAppData [jmxurl=" + jmxurl + ", host=" + host + ", urlScheme=" + urlScheme + ", port="
+ port + ", keepChecking=" + keepChecking + "]";
}
@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());
result = prime * result + (keepChecking ? 1231 : 1237);
result = prime * result + ((port == null) ? 0 : port.hashCode());
result = prime * result + ((urlScheme == null) ? 0 : urlScheme.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;
if (keepChecking != other.keepChecking)
return false;
if (port == null) {
if (other.port != null)
return false;
} else if (!port.equals(other.port))
return false;
if (urlScheme == null) {
if (other.urlScheme != null)
return false;
} else if (!urlScheme.equals(other.urlScheme))
return false;
return true;
}
}
private static Logger logger = LoggerFactory.getLogger(SpringProcessConnectorRemote.class);
/**
* 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 final Map<RemoteBootAppData, String> remoteAppInstances;
private final SpringProcessConnectorService processConnectorService;
private final SpringProcessLiveDataProvider liveDataProvider;
public SpringProcessConnectorRemote(SimpleLanguageServer server, SpringProcessConnectorService processConnector,
SpringProcessLiveDataProvider liveDataProvider) {
this.processConnectorService = processConnector;
this.liveDataProvider = liveDataProvider;
this.remoteAppInstances = new HashMap<>();
server.getWorkspaceService().onDidChangeConfiguraton(this::handleSettings);
}
private synchronized void handleSettings(Settings settings) {
RemoteBootAppData[] appData = settings.getAs(RemoteBootAppData[].class, "boot-java", "remote-apps");
if (appData == null) {
//Avoid NPE
appData = new RemoteBootAppData[0];
}
// remove outdated remote apps
Set<RemoteBootAppData> newAppData = new HashSet<>(Arrays.asList(appData));
Iterator<Entry<RemoteBootAppData, String>> entries = remoteAppInstances.entrySet().iterator();
while (entries.hasNext()) {
Entry<RemoteBootAppData, String> entry = entries.next();
RemoteBootAppData key = entry.getKey();
if (!newAppData.contains(key)) {
logger.info("Removing RemoteSpringBootApp: "+key);
entries.remove();
// create meaningful process key here
String processKey = entry.getValue();
processConnectorService.disconnectProcess(processKey);
}
}
//Add new apps
for (RemoteBootAppData data : newAppData) {
remoteAppInstances.computeIfAbsent(data, (_appData) -> {
logger.info("Creating RemoteStringBootApp: " + _appData);
String processKey = getProcessKey(_appData);
String processID = null;
String processName = null;
String jmxURL = _appData.getJmxurl();
String host = _appData.getHost();
String port = _appData.getPort();
String urlScheme = _appData.getUrlScheme();
boolean keepChecking = _appData.isKeepChecking();
SpringProcessConnectorOverJMX connector = new SpringProcessConnectorOverJMX(liveDataProvider, processKey, jmxURL, urlScheme, processID, processName, host, port, keepChecking);
processConnectorService.connectProcess(processKey, connector);
return processKey;
});
}
}
private static String getProcessKey(RemoteBootAppData appData) {
return "remote";
}
}

View File

@@ -0,0 +1,136 @@
/*******************************************************************************
* 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
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.boot.java.livehover.v2;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author Martin Lippert
*/
public class SpringProcessConnectorService {
private static final Logger log = LoggerFactory.getLogger(SpringProcessConnectorService.class);
private static final int RETRY_MAX_NO = 10;
private static final int RETRY_DELAY_IN_SECONDS = 3;
private final ScheduledThreadPoolExecutor scheduler;
private final ConcurrentMap<String, SpringProcessConnector> connectors;
public SpringProcessConnectorService() {
this.scheduler = new ScheduledThreadPoolExecutor(10);
this.connectors = new ConcurrentHashMap<>();
}
public void connectProcess(String processKey, SpringProcessConnector connector) {
log.info("connect to process: " + processKey);
this.connectors.put(processKey, connector);
try {
scheduleConnect(processKey, connector, 0, TimeUnit.SECONDS, 0);
}
catch (Exception e) {
log.error("error connecting to " + processKey, e);
}
}
public void refreshProcess(String processKey) {
log.info("refresh process: " + processKey);
SpringProcessConnector connector = this.connectors.get(processKey);
if (connector != null) {
scheduleRefresh(processKey, connector, 0, TimeUnit.SECONDS, 0);
}
}
public void disconnectProcess(String processKey) {
log.info("disconnect from process: " + processKey);
SpringProcessConnector connector = this.connectors.remove(processKey);
if (connector != null) {
scheduleDisconnect(processKey, connector, 0, TimeUnit.SECONDS, 0);
}
}
public SpringProcessConnector[] getConnectedProcesses() {
return (SpringProcessConnector[]) this.connectors.values().toArray(new SpringProcessConnector[this.connectors.values().size()]);
}
/**
* common method to generate process keys from process IDs and process names
*/
public static String getProcessKey(String processID, String processName) {
return processID + " - " + processName;
}
private void scheduleConnect(String processKey, SpringProcessConnector connector, long delay, TimeUnit unit, int retryNo) {
log.info("schedule task to connect to process: " + processKey + " - retry no: " + retryNo);
this.scheduler.schedule(() -> {
try {
connector.connect();
}
catch (Exception e) {
log.info("problem occured during process connect", e);
if (retryNo < RETRY_MAX_NO) {
scheduleConnect(processKey, connector, RETRY_DELAY_IN_SECONDS, TimeUnit.SECONDS, retryNo + 1);
}
}
refreshProcess(processKey);
}, delay, unit);
}
private void scheduleDisconnect(String processKey, SpringProcessConnector connector, long delay, TimeUnit unit, int retryNo) {
log.info("schedule task to disconnect from process: " + processKey + " - retry no: " + retryNo);
this.scheduler.schedule(() -> {
try {
connector.disconnect();
}
catch (Exception e) {
log.info("problem occured during process disconnect", e);
if (retryNo < RETRY_MAX_NO) {
scheduleDisconnect(processKey, connector, RETRY_DELAY_IN_SECONDS, TimeUnit.SECONDS, retryNo + 1);
}
}
}, delay, unit);
}
private void scheduleRefresh(String processKey, SpringProcessConnector connector, long delay, TimeUnit unit, int retryNo) {
log.info("schedule task to refresh data from process: " + processKey + " - retry no: " + retryNo);
this.scheduler.schedule(() -> {
try {
connector.refresh();
}
catch (Exception e) {
log.info("problem occured during process live data refresh", e);
if (retryNo < RETRY_MAX_NO) {
scheduleRefresh(processKey, connector, 3, TimeUnit.SECONDS, retryNo + 1);
}
else {
disconnectProcess(processKey);
}
}
}, delay, unit);
}
}

View File

@@ -0,0 +1,93 @@
/*******************************************************************************
* 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
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.boot.java.livehover.v2;
/**
* @author Martin Lippert
*/
public class SpringProcessLiveData {
private final String processName;
private final String processID;
private final String contextPath;
private final String urlScheme;
private final String port;
private final String host;
private final LiveBeansModel beansModel;
private final String[] activeProfiles;
private final LiveRequestMapping[] requestMappings;
private final LiveConditional[] conditionals;
private final LiveProperties properties;
public SpringProcessLiveData(String processName, String processID, String contextPath, String urlScheme,
String port, String host, LiveBeansModel beansModel, String[] activeProfiles,
LiveRequestMapping[] requestMappings, LiveConditional[] conditionals, LiveProperties properties) {
super();
this.processName = processName;
this.processID = processID;
this.contextPath = contextPath;
this.urlScheme = urlScheme;
this.port = port;
this.host = host;
this.beansModel = beansModel;
this.activeProfiles = activeProfiles;
this.requestMappings = requestMappings;
this.conditionals = conditionals;
this.properties = properties;
}
public String getProcessName() {
return this.processName;
}
public String getProcessID() {
return this.processID;
}
public String getContextPath() {
return this.contextPath;
}
public String getUrlScheme() {
return this.urlScheme;
}
public String getPort() {
return this.port;
}
public String getHost() {
return this.host;
}
public LiveBeansModel getBeans() {
return this.beansModel;
}
public String[] getActiveProfiles() {
return this.activeProfiles;
}
public LiveRequestMapping[] getRequestMappings() {
return this.requestMappings;
}
public LiveConditional[] getLiveConditionals() {
return this.conditionals;
}
public LiveProperties getLiveProperties() {
return this.properties;
}
}

View File

@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2018 Pivotal, Inc.
* 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
@@ -8,24 +8,21 @@
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.commons.boot.app.cli.requestmappings;
package org.springframework.ide.vscode.boot.java.livehover.v2;
import java.util.Arrays;
/**
* @author Martin Lippert
*/
public class SpringProcessLiveDataChangeEvent {
private final SpringProcessLiveData[] updatedLiveData;
public class ParseUtil {
public static String[] splitPaths(String paths) {
return Arrays.stream(paths.split("\\|\\|"))
.map(s -> s.trim())
.filter(s -> !s.isEmpty())
.map(s -> {
if (s.charAt(0) != '/') {
return '/' + s;
} else {
return s;
}
})
.toArray(String[]::new);
public SpringProcessLiveDataChangeEvent(SpringProcessLiveData[] updatedLiveData) {
this.updatedLiveData = updatedLiveData;
}
public SpringProcessLiveData[] getUpdatedLiveData() {
return updatedLiveData;
}
}

View File

@@ -0,0 +1,20 @@
/*******************************************************************************
* 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
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.boot.java.livehover.v2;
/**
* @author Martin Lippert
*/
public interface SpringProcessLiveDataChangeListener {
void liveDataChanged(SpringProcessLiveDataChangeEvent liveDataChangeEvent);
}

View File

@@ -0,0 +1,526 @@
/*******************************************************************************
* 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
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.boot.java.livehover.v2;
import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.lang.management.RuntimeMXBean;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import javax.management.InstanceNotFoundException;
import javax.management.MBeanServerConnection;
import javax.management.ObjectName;
import javax.management.Query;
import javax.management.QueryExp;
import javax.management.remote.JMXConnector;
import org.json.JSONArray;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ide.vscode.commons.boot.app.cli.ContextPath;
import org.springframework.ide.vscode.commons.util.StringUtil;
import com.google.common.collect.ImmutableList;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
/**
* @author Martin Lippert
*/
public class SpringProcessLiveDataExtractorOverJMX {
private static final Logger log = LoggerFactory.getLogger(SpringProcessLiveDataExtractorOverJMX.class);
// NOTE: Gson-based serialisation replaces the old Jackson ObjectMapper. Not sure if this makes a difference in the long run, but to retain the same output that Jackson Object Mapper
// was generating during serialisation, some configuration in Gson is required, as the default behaviour of Gson is different than Object Mapper.
// Namely: Object Mapper does not escape Html, whereas Gson does by default (for example
// '=' in Gson appears as '\u003d')
private final Gson gson = new GsonBuilder()
.disableHtmlEscaping()
.create();
/**
* @param processID if null, will be determined searching existing mbeans for that information (for remote processes via platform beans runtime name)
* @param processName if null, will be determined searching existing mbeans for that information (for remote processes infering the java command from the system properties)
* @param urlScheme should always be != null
* @param host should always be != null
* @param contextPath if null, will be determined searching existing mbeans for that information (for local processes)
* @param port if null, will be determined searching existing mbeans for that information (for local processes)
*/
public SpringProcessLiveData retrieveLiveData(JMXConnector jmxConnector, String processID, String processName,
String urlScheme, String host, String contextPath, String port) {
try {
MBeanServerConnection connection = jmxConnector.getMBeanServerConnection();
String domain = getDomainForActuator(connection);
String environment = getEnvironment(connection, domain);
String[] activeProfiles = getActiveProfiles(connection, environment);
LiveProperties properties = getProperties(connection, environment);
if (processID == null) {
processID = getProcessID(connection);
}
if (processName == null) {
Properties systemProperties = getSystemProperties(connection);
if (systemProperties != null) {
String javaCommand = getJavaCommand(systemProperties);
processName = getProcessName(javaCommand);
}
}
LiveConditional[] conditionals = getConditionals(connection, domain, processID, processName);
LiveRequestMapping[] requestMappings = getRequestMappings(connection, domain);
LiveBeansModel beans = getBeans(connection, domain);
if (contextPath == null) {
contextPath = getContextPath(connection, domain, environment);
}
if (port == null) {
port = getPort(connection, environment);
}
return new SpringProcessLiveData(
processName,
processID,
contextPath,
urlScheme,
port,
host,
beans,
activeProfiles,
requestMappings,
conditionals,
properties);
}
catch (Exception e) {
log.error("error reading live data from: " + processID + " - " + processName, e);
}
return null;
}
public String getProcessID(MBeanServerConnection connection) {
try {
RuntimeMXBean runtime = ManagementFactory.getPlatformMXBean(connection, RuntimeMXBean.class);
return runtime.getName();
} catch (Exception e) {
}
return null;
}
public String getProcessName(String command) throws Exception {
if (command != null) {
int space = command.indexOf(' ');
if (space >= 0) {
command = command.substring(0, space);
}
command = command.trim();
if (!"".equals(command)) {
return command;
}
}
return "Unknown";
}
public Properties getSystemProperties(MBeanServerConnection connection) {
try {
RuntimeMXBean runtime = ManagementFactory.getPlatformMXBean(connection, RuntimeMXBean.class);
Properties props = new Properties();
for (Entry<String, String> e : runtime.getSystemProperties().entrySet()) {
props.put(e.getKey(), e.getValue());
}
return props;
}
catch (Exception e) {
log.error("error fetching system properties", e);
}
return null;
}
public String getJavaCommand(Properties systemProperties) {
return (String) systemProperties.get("sun.java.command");
}
public LiveBeansModel getBeans(MBeanServerConnection connection, String domain) {
try {
Object json = null;
try {
json = getBeansFromActuator(connection, domain);
if (json == null) {
json = getBeansFromNonBootMBean(connection);
}
} catch (IOException e) {
// PT 160096886 - Don't throw exception, as actuator info will not be available when app stopping, and
// this is not an error condition. Return empty model instead.
} catch (ExecutionException e) {
if (!(e.getCause() instanceof IOException)) {
throw e;
}
}
if (json != null) {
if (json instanceof String) {
return LiveBeansModel.parse((String)json);
}
else {
return LiveBeansModel.parse(gson.toJson(json));
}
}
} catch (Exception e) {
log.error("Error parsing beans", e);
}
return LiveBeansModel.builder().build();
}
protected Object getBeansFromNonBootMBean(MBeanServerConnection connection) throws Exception {
Set<ObjectName> nonBootSpringLiveMBeans = getNonBootSpringLiveMBeans(connection);
if (nonBootSpringLiveMBeans.size() > 0) {
return getActuatorDataFromAttribute(connection, nonBootSpringLiveMBeans.iterator().next(), "SnapshotAsJson");
}
else {
return null;
}
}
private Object getBeansFromActuator(MBeanServerConnection connection, String domain) throws Exception {
Object result = getActuatorDataFromOperation(connection, getObjectName(domain, "type=Endpoint,name=Beans"), "beans");
if (result != null) return result;
return getActuatorDataFromAttribute(connection, getObjectName(domain, "type=Endpoint,name=beansEndpoint"), "Data");
}
private Set<ObjectName> getNonBootSpringLiveMBeans(MBeanServerConnection connection) {
try {
QueryExp queryExp = Query.isInstanceOf(Query.value("org.springframework.context.support.LiveBeansView"));
return connection.queryNames(null, queryExp);
} catch (Exception e) {
log.error("error searching for non-boot spring mbeans", e);
}
return Collections.emptySet();
}
public LiveRequestMapping[] getRequestMappings(MBeanServerConnection connection, String domain) throws Exception {
try {
//Boot 2.x
Object result = getActuatorDataFromOperation(connection, getObjectName(domain, "type=Endpoint,name=Mappings"), "mappings");
if (result != null) {
String mappings = gson.toJson(result);
return parseRequestMappingsJson(mappings, "2.x");
}
//Boot 1.x
result = getActuatorDataFromAttribute(connection, getObjectName(domain, "type=Endpoint,name=requestMappingEndpoint"), "Data");
if (result != null) {
String mappings = gson.toJson(result);
return parseRequestMappingsJson(mappings, "1.x");
}
} catch (IOException e) {
//ignore.. app stopped
} catch (ExecutionException e) {
if (!(e.getCause() instanceof IOException)) {
throw e;
}
}
return null;
}
private LiveRequestMapping[] parseRequestMappingsJson(String json, String bootVersion) {
JSONObject obj = new JSONObject(json);
if (bootVersion.equals("2.x")) {
return LiveRequestMappingBoot2xParser.parse(obj);
} else { //1.x
List<LiveRequestMapping> result = new ArrayList<>();
Iterator<String> keys = obj.keys();
while (keys.hasNext()) {
String rawKey = keys.next();
JSONObject value = obj.getJSONObject(rawKey);
result.add(new LiveRequestMappingBoot1xRequestMapping(rawKey, value));
}
return (LiveRequestMapping[]) result.toArray(new LiveRequestMapping[result.size()]);
}
}
public LiveConditional[] getConditionals(MBeanServerConnection connection, String domain, String processId, String processName) {
try {
//Boot 2.x
Object result = getActuatorDataFromOperation(connection, getObjectName(domain, "type=Endpoint,name=Conditions"), "applicationConditionEvaluation");
if (result != null) {
String report = gson.toJson(result);
return LiveConditionalParser.parse(report, processId, processName);
}
//Boot 1.x
result = getActuatorDataFromAttribute(connection, getObjectName(domain, "type=Endpoint,name=autoConfigurationReportEndpoint"), "Data");
if (result != null) {
String report = gson.toJson(result);
return LiveConditionalParser.parse(report, processId, processName);
}
} catch (IOException e) {
//ignore. Happens a lot when apps are stopped while we try to talk to them.
} catch (Exception e) {
// TODO Auto-generated catch block
}
return null;
}
public String[] getActiveProfiles(MBeanServerConnection connection, String environment) {
try {
if (environment != null) {
JSONObject env = new JSONObject(environment);
Object _profiles = env.opt("activeProfiles"); //Boot 2.0
if (_profiles == null) {
_profiles = env.opt("profiles"); //Boot 1.5
}
if (_profiles instanceof JSONArray) {
JSONArray profiles = (JSONArray) _profiles;
ImmutableList.Builder<String> list = ImmutableList.builder();
for (Object object : profiles) {
if (object instanceof String) {
list.add((String) object);
}
}
return list.build().toArray(new String[0]);
}
}
} catch (Exception e) {
log.error("error resolving profiles from env", e);
}
return null;
}
public LiveProperties getProperties(MBeanServerConnection connection, String environment) throws Exception {
try {
if (environment != null) {
return LivePropertiesJsonParser.parseProperties(environment);
}
} catch (Exception e) {
log.error("error resolving live properties from environment endpoint", e);
}
return null;
}
public String getEnvironment(MBeanServerConnection connection, String domain) throws Exception {
try {
Object result = getActuatorDataFromAttribute(connection, getObjectName(domain, "type=Endpoint,name=environmentEndpoint"), "Data");
if (result != null) {
String environment = gson.toJson(result);
return environment;
}
result = getActuatorDataFromOperation(connection, getObjectName(domain, "type=Endpoint,name=Env"), "environment");
if (result != null) {
String environment = gson.toJson(result);
return environment;
}
} catch (IOException e) {
//ignore... probably just because app is stopped
} catch (ExecutionException e) {
if (!(e.getCause() instanceof IOException)) {
throw e;
}
}
return null;
}
private Object getActuatorDataFromAttribute(MBeanServerConnection connection, ObjectName objectName, String attribute) throws Exception {
if (objectName != null) {
try {
return connection.getAttribute(objectName, attribute);
}
catch (InstanceNotFoundException|IOException e) {
return null;
}
}
return null;
}
private Object getActuatorDataFromOperation(MBeanServerConnection connection, ObjectName objectName, String operation) throws Exception {
if (objectName != null) {
try {
return connection.invoke(objectName, operation, null, null);
}
catch (InstanceNotFoundException|IOException e) {
return null;
}
}
return null;
}
private ObjectName getObjectName(String domain, String keyProperties) throws Exception {
if (StringUtil.hasText(domain) && StringUtil.hasText(keyProperties)) {
String fullName = domain + ":" + keyProperties;
return ObjectName.getInstance(fullName);
}
return null;
}
/**
* PT 156072399: Actuator information can be defined using a different JMX MBean domain.
* By default, Spring Boot exposes management endpoints as JMX MBeans under the 'org.springframework.boot' domain.
* Users can however define another domain in the app's application.properties, for example using this property:
* management.endpoints.jmx.domain=com.example.myapp
* <b/>
* Therefore we need to support other domains than just: 'org.springframework.boot'
* @return JMX MBean domain containing actuator information, or null if not resolved.
* @throws Exception when resolving domain from JMX
*/
public String getDomainForActuator(MBeanServerConnection connection) throws Exception {
QueryExp queryExp = Query.or(Query.or(Query.isInstanceOf(Query.value("org.springframework.boot.actuate.endpoint.jmx.EndpointMBean")),
Query.isInstanceOf(Query.value("org.springframework.boot.actuate.endpoint.jmx.DataEndpointMBean"))),
Query.isInstanceOf(Query.value("org.springframework.context.support.LiveBeansView")));
Set<ObjectName> names = connection.queryNames(null, queryExp);
if (names != null && names.size() > 0) {
return names.iterator().next().getDomain();
}
else {
return null;
}
}
public String getPort(MBeanServerConnection connection, String environment) throws Exception {
String port = getPortViaAdmin(connection);
if (port != null) {
return port;
}
port = getPortViaActuator(connection, environment);
if (port != null) {
return port;
}
port = getPortViaTomcatBean(connection);
return port;
}
public String getContextPath(MBeanServerConnection connection, String domain, String environment) throws Exception {
try {
String bootVersion = null;
// Boot 1.x
Object result = getActuatorDataFromAttribute(connection, getObjectName(domain, "type=Endpoint,name=requestMappingEndpoint"), "Data");
if (result != null) {
bootVersion = "1.x";
}
// Boot 2.x
result = getActuatorDataFromOperation(connection, getObjectName(domain, "type=Endpoint,name=Mappings"), "mappings");
if (result != null) {
bootVersion = "2.x";
}
return bootVersion != null && environment != null ? ContextPath.getContextPath(bootVersion, environment) : null;
} catch (IOException e) {
//Ignore... happens a low when app is stopped
} catch (ExecutionException e) {
if (!(e.getCause() instanceof IOException)) {
throw e;
}
}
return null;
}
private String getPortViaAdmin(MBeanServerConnection connection) throws Exception {
try {
String DEFAULT_OBJECT_NAME = "org.springframework.boot:type=Admin,name=SpringApplication";
ObjectName objectName = new ObjectName(DEFAULT_OBJECT_NAME);
Object o = connection.invoke(objectName,"getProperty", new String[] {"local.server.port"}, new String[] {String.class.getName()});
return o==null ? null : o.toString();
}
catch (InstanceNotFoundException e) {
return null;
}
}
private String getPortViaActuator(MBeanServerConnection connection, String environment) throws Exception {
if (environment != null) {
JSONObject env = new JSONObject(environment);
if (env != null) {
JSONObject portsObject = env.optJSONObject("server.ports");
if (portsObject != null) {
String portValue = portsObject.optString("local.server.port");
if (portValue!=null) {
return portValue;
}
}
//Not found as direct property value... in Boot 2.0 we must look inside the 'propertySources'.
//Similar... but structure is more complex.
JSONArray propertySources = env.optJSONArray("propertySources");
if (propertySources!=null) {
for (Object _source : propertySources) {
if (_source instanceof JSONObject) {
JSONObject source = (JSONObject) _source;
String sourceName = source.optString("name");
if ("server.ports".equals(sourceName)) {
JSONObject props = source.optJSONObject("properties");
JSONObject valueObject = props.optJSONObject("local.server.port");
if (valueObject!=null) {
String portValue = valueObject.optString("value");
if (portValue!=null) {
return portValue;
}
}
}
}
}
}
}
}
return null;
}
private String getPortViaTomcatBean(MBeanServerConnection connection) throws Exception {
try {
Set<ObjectName> queryNames = connection.queryNames(null, null);
for (ObjectName objectName : queryNames) {
if (objectName.toString().startsWith("Tomcat") && objectName.toString().contains("type=Connector")) {
Object result = connection.getAttribute(objectName, "localPort");
if (result != null) {
return result.toString();
}
}
}
}
catch (InstanceNotFoundException e) {
}
return null;
}
}

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
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.boot.java.livehover.v2;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* @author Martin Lippert
*/
public class SpringProcessLiveDataProvider {
private final ConcurrentMap<String, SpringProcessLiveData> liveData;
private final List<SpringProcessLiveDataChangeListener> listeners;
public SpringProcessLiveDataProvider() {
this.liveData = new ConcurrentHashMap<>();
this.listeners = new CopyOnWriteArrayList<>();
}
public SpringProcessLiveData[] getLatestLiveData() {
Collection<SpringProcessLiveData> values = this.liveData.values();
return (SpringProcessLiveData[]) values.toArray(new SpringProcessLiveData[values.size()]);
}
/**
* add the process live data, if there is not already process live data associated with the process key
* @return true, if the data was added (and was new), otherwise false
*/
public boolean add(String processKey, SpringProcessLiveData liveData) {
SpringProcessLiveData oldData = this.liveData.putIfAbsent(processKey, liveData);
if (oldData == null) {
announceChangedLiveData();
}
return oldData == null;
}
public void remove(String processKey) {
SpringProcessLiveData removed = this.liveData.remove(processKey);
if (removed != null) {
announceChangedLiveData();
}
}
public void update(String processKey, SpringProcessLiveData liveData) {
this.liveData.put(processKey, liveData);
announceChangedLiveData();
}
public void addLiveDataChangeListener(SpringProcessLiveDataChangeListener listener) {
this.listeners.add(listener);
}
public void removeLiveDataChangeListener(SpringProcessLiveDataChangeListener listener) {
this.listeners.remove(listener);
}
private void announceChangedLiveData() {
SpringProcessLiveData[] latestLiveData = getLatestLiveData();
SpringProcessLiveDataChangeEvent event = new SpringProcessLiveDataChangeEvent(latestLiveData);
for (SpringProcessLiveDataChangeListener listener : this.listeners) {
listener.liveDataChanged(event);
}
}
}

View File

@@ -0,0 +1,164 @@
/*******************************************************************************
* 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
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.boot.java.livehover.v2;
import java.util.Arrays;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicReference;
import org.eclipse.lsp4j.CodeLens;
import org.eclipse.lsp4j.VersionedTextDocumentIdentifier;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ide.vscode.boot.java.BootJavaLanguageServerComponents;
import org.springframework.ide.vscode.boot.java.handlers.BootJavaHoverProvider;
import org.springframework.ide.vscode.commons.java.IJavaProject;
import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder;
import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer;
import org.springframework.ide.vscode.commons.protocol.HighlightParams;
import org.springframework.ide.vscode.commons.util.text.TextDocument;
/**
* @author Martin Lippert
*/
public class SpringProcessLiveHoverUpdater {
private static final Logger log = LoggerFactory.getLogger(SpringProcessLiveHoverUpdater.class);
private final SimpleLanguageServer server;
private final BootJavaHoverProvider hoverProvider;
private boolean highlightsEnabled = true;
private JavaProjectFinder projectFinder;
private final Map<String, AtomicReference<IJavaProject>> watchedDocs;
public SpringProcessLiveHoverUpdater(
SimpleLanguageServer server,
BootJavaHoverProvider hoverProvider,
JavaProjectFinder projectFinder,
SpringProcessLiveDataProvider liveDataProvider) {
this.server = server;
this.hoverProvider = hoverProvider;
this.projectFinder = projectFinder;
this.watchedDocs = new ConcurrentHashMap<>();
server.getTextDocumentService().onDidChangeContent(params -> {
TextDocument doc = params.getDocument();
if (BootJavaLanguageServerComponents.LANGUAGES.contains(doc.getLanguageId())) {
try {
watchDocument(doc.getUri());
} catch (Throwable t) {
log.error("", t);
}
}
});
server.getTextDocumentService().onDidClose(doc -> {
unwatchDocument(doc.getUri());
});
liveDataProvider.addLiveDataChangeListener(event -> {
update();
});
}
public void cleanup() {
watchedDocs.keySet().forEach(uri -> cleanupLiveHints(uri));
}
public void watchDocument(String docURI) {
this.watchedDocs.putIfAbsent(docURI, new AtomicReference<IJavaProject>());
updateDoc(docURI);
}
public void unwatchDocument(String docURI) {
this.watchedDocs.remove(docURI);
cleanupLiveHints(docURI);
}
private void updateDoc(String docURI) {
try {
IJavaProject project = getCachedProject(docURI);
update(docURI, project);
}
catch (Exception e) {
log.error("", e);
}
}
private void update() {
if (this.watchedDocs.size() > 0) {
try {
for (String docURI : watchedDocs.keySet()) {
IJavaProject project = getCachedProject(docURI);
update(docURI, project);
}
} catch (Exception e) {
log.error("", e);
}
}
}
private void update(String docURI, IJavaProject project) {
if (highlightsEnabled) {
try {
TextDocument doc = this.server.getTextDocumentService().get(docURI);
if (doc != null) {
CodeLens[] infos = this.hoverProvider.getLiveHoverHints(doc, project);
publishLiveHints(docURI, infos);
}
} catch (Exception e) {
log.error("", e);
}
}
}
private IJavaProject getCachedProject(String docURI) {
AtomicReference<IJavaProject> reference = this.watchedDocs.get(docURI);
if (reference != null) {
IJavaProject project = reference.get();
if (project == null) {
project = identifyProject(docURI);
if (!reference.compareAndSet(null, project)) {
return reference.get();
}
}
return project;
}
return null;
}
private IJavaProject identifyProject(String docURI) {
TextDocument doc = this.server.getTextDocumentService().get(docURI);
if (doc != null) {
return projectFinder.find(doc.getId()).orElse(null);
}
else {
return null;
}
}
private void publishLiveHints(String docURI, CodeLens[] codeLenses) {
TextDocument doc = server.getTextDocumentService().get(docURI);
if (doc != null) {
int version = doc.getVersion();
VersionedTextDocumentIdentifier id = new VersionedTextDocumentIdentifier(docURI, version);
server.getClient().highlight(new HighlightParams(id, Arrays.asList(codeLenses)));
}
}
private void cleanupLiveHints(String docURI) {
publishLiveHints(docURI, new CodeLens[0]);
}
}

View File

@@ -0,0 +1,81 @@
/*******************************************************************************
* 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
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.boot.java.livehover.v2;
import java.time.Duration;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author Martin Lippert
*/
public class SpringProcessTracker {
private static final Duration DEFAULT_INTERVAL = Duration.ofMillis(5000);
private static final Logger log = LoggerFactory.getLogger(SpringProcessTracker.class);
private final long POLLING_INTERVAL_MILLISECONDS;
private final SpringProcessConnectorLocal localProcessConnector;
private boolean automaticTrackingEnabled;
private ScheduledThreadPoolExecutor timer;
public SpringProcessTracker(SpringProcessConnectorLocal localProcessConnector, Duration pollingInterval) {
this.localProcessConnector = localProcessConnector;
this.POLLING_INTERVAL_MILLISECONDS = pollingInterval == null ? DEFAULT_INTERVAL.toMillis() : pollingInterval.toMillis();
this.automaticTrackingEnabled = true;
}
public synchronized void setTrackingEnabled(boolean trackingEnabled) {
if (automaticTrackingEnabled != trackingEnabled) {
automaticTrackingEnabled = trackingEnabled;
refresh();
}
}
public synchronized void start() {
if (automaticTrackingEnabled && timer == null) {
log.info("Starting SpringProcessTracker");
this.timer = new ScheduledThreadPoolExecutor(1);
this.timer.scheduleWithFixedDelay(() -> this.update(), 0, POLLING_INTERVAL_MILLISECONDS, TimeUnit.MILLISECONDS);
}
}
public synchronized void stop() {
if (timer != null) {
log.info("Shutting down SpringProcessTracker");
timer.shutdown();
timer = null;
}
}
private void update() {
log.info("scan for local processes cycle...");
try {
this.localProcessConnector.searchForNewProcesses();
}
catch (Throwable e) {
log.error("error searching for local processes", e);
}
}
private void refresh() {
if (automaticTrackingEnabled) {
start();
} else {
stop();
}
}
}

View File

@@ -21,8 +21,8 @@ import org.eclipse.lsp4j.SymbolKind;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ide.vscode.boot.java.handlers.RunningAppProvider;
import org.springframework.ide.vscode.boot.java.livehover.v2.LiveRequestMapping;
import org.springframework.ide.vscode.commons.boot.app.cli.SpringBootApp;
import org.springframework.ide.vscode.commons.boot.app.cli.requestmappings.RequestMapping;
/**
* @author Martin Lippert
@@ -40,36 +40,36 @@ public class LiveAppURLSymbolProvider {
public List<? extends SymbolInformation> getSymbols(String query) {
List<SymbolInformation> result = new ArrayList<>();
try {
SpringBootApp[] runningApps = runningAppProvider.getAllRunningSpringApps().toArray(new SpringBootApp[0]);
for (SpringBootApp app : runningApps) {
try {
String urlScheme = app.getUrlScheme();
String host = app.getHost();
String port = app.getPort();
String contextPath = app.getContextPath();
for (RequestMapping rm : app.getRequestMappings()) {
String[] paths = rm.getSplitPath();
if (paths==null || paths.length==0) {
//Technically, this means the path 'predicate' is unconstrained, meaning any path matches.
//So this is not quite the same as the case where path=""... but...
//It is better for us to show one link where any path is allowed, versus showing no links where any link is allowed.
//So we'll pretend this is the same as path="" as that gives a working link.
paths = new String[] {""};
}
for (String path : paths) {
String url = UrlUtil.createUrl(urlScheme, host, port, path, contextPath);
result.add(new SymbolInformation(url, SymbolKind.Method, new Location(url, new Range(new Position(0, 0), new Position(0, 1)))));
}
}
}
catch (Exception e) {
log.error("", e);
}
}
} catch (Exception e) {
log.error("", e);
}
// try {
// SpringBootApp[] runningApps = runningAppProvider.getAllRunningSpringApps().toArray(new SpringBootApp[0]);
// for (SpringBootApp app : runningApps) {
// try {
// String urlScheme = app.getUrlScheme();
// String host = app.getHost();
// String port = app.getPort();
// String contextPath = app.getContextPath();
// for (LiveRequestMapping rm : app.getRequestMappings()) {
// String[] paths = rm.getSplitPath();
// if (paths==null || paths.length==0) {
// //Technically, this means the path 'predicate' is unconstrained, meaning any path matches.
// //So this is not quite the same as the case where path=""... but...
// //It is better for us to show one link where any path is allowed, versus showing no links where any link is allowed.
// //So we'll pretend this is the same as path="" as that gives a working link.
// paths = new String[] {""};
// }
// for (String path : paths) {
// String url = UrlUtil.createUrl(urlScheme, host, port, path, contextPath);
// result.add(new SymbolInformation(url, SymbolKind.Method, new Location(url, new Range(new Position(0, 0), new Position(0, 1)))));
// }
// }
// }
// catch (Exception e) {
// log.error("", e);
// }
// }
// } catch (Exception e) {
// log.error("", e);
// }
return result;
}

View File

@@ -31,8 +31,8 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.requestmappings.RequestMapping;
import org.springframework.ide.vscode.boot.java.livehover.v2.LiveRequestMapping;
import org.springframework.ide.vscode.boot.java.livehover.v2.SpringProcessLiveData;
import org.springframework.ide.vscode.commons.java.IJavaProject;
import org.springframework.ide.vscode.commons.util.Renderable;
import org.springframework.ide.vscode.commons.util.Renderables;
@@ -57,15 +57,15 @@ public class RequestMappingHoverProvider implements HoverProvider {
@Override
public Hover provideHover(ASTNode node, Annotation annotation,
ITypeBinding type, int offset, TextDocument doc, IJavaProject project, SpringBootApp[] runningApps) {
return provideHover(annotation, doc, runningApps);
ITypeBinding type, int offset, TextDocument doc, IJavaProject project, SpringProcessLiveData[] processLiveData) {
return provideHover(annotation, doc, processLiveData);
}
@Override
public Collection<CodeLens> getLiveHintCodeLenses(IJavaProject project, Annotation annotation, TextDocument doc, SpringBootApp[] runningApps) {
public Collection<CodeLens> getLiveHintCodeLenses(IJavaProject project, Annotation annotation, TextDocument doc, SpringProcessLiveData[] processLiveData) {
try {
if (runningApps.length > 0) {
List<Tuple2<RequestMapping, SpringBootApp>> val = getRequestMappingMethodFromRunningApp(annotation, runningApps);
if (processLiveData.length > 0) {
List<Tuple2<LiveRequestMapping, SpringProcessLiveData>> val = getRequestMappingMethodFromRunningApp(annotation, processLiveData);
if (!val.isEmpty()) {
Range hoverRange = doc.toRange(annotation.getStartPosition(), annotation.getLength());
List<String> urls = getUrls(val);
@@ -150,10 +150,10 @@ public class RequestMappingHoverProvider implements HoverProvider {
return lenses;
}
private Hover provideHover(Annotation annotation, TextDocument doc, SpringBootApp[] runningApps) {
private Hover provideHover(Annotation annotation, TextDocument doc, SpringProcessLiveData[] processLiveData) {
try {
List<Tuple2<RequestMapping, SpringBootApp>> val = getRequestMappingMethodFromRunningApp(annotation, runningApps);
List<Tuple2<LiveRequestMapping, SpringProcessLiveData>> val = getRequestMappingMethodFromRunningApp(annotation, processLiveData);
if (!val.isEmpty()) {
Hover hover = createHoverWithContent(val);
@@ -171,17 +171,17 @@ public class RequestMappingHoverProvider implements HoverProvider {
return null;
}
private List<Tuple2<RequestMapping, SpringBootApp>> getRequestMappingMethodFromRunningApp(Annotation annotation,
SpringBootApp[] runningApps) {
private List<Tuple2<LiveRequestMapping, SpringProcessLiveData>> getRequestMappingMethodFromRunningApp(Annotation annotation,
SpringProcessLiveData[] processLiveData) {
List<Tuple2<RequestMapping, SpringBootApp>> results = new ArrayList<>();
List<Tuple2<LiveRequestMapping, SpringProcessLiveData>> results = new ArrayList<>();
try {
for (SpringBootApp app : runningApps) {
Collection<RequestMapping> mappings = app.getRequestMappings();
if (mappings != null && !mappings.isEmpty()) {
mappings.stream()
for (SpringProcessLiveData liveData : processLiveData) {
LiveRequestMapping[] mappings = liveData.getRequestMappings();
if (mappings != null && mappings.length > 0) {
Arrays.stream(mappings)
.filter(rm -> methodMatchesAnnotation(annotation, rm))
.map(rm -> Tuples.of(rm, app))
.map(rm -> Tuples.of(rm, liveData))
.findFirst().ifPresent(t -> results.add(t));
}
}
@@ -191,7 +191,7 @@ public class RequestMappingHoverProvider implements HoverProvider {
return results;
}
private boolean methodMatchesAnnotation(Annotation annotation, RequestMapping rm) {
private boolean methodMatchesAnnotation(Annotation annotation, LiveRequestMapping rm) {
String rqClassName = rm.getFullyQualifiedClassName();
if (rqClassName != null) {
@@ -222,16 +222,16 @@ public class RequestMappingHoverProvider implements HoverProvider {
return false;
}
private List<String> getUrls(List<Tuple2<RequestMapping, SpringBootApp>> mappingMethods) throws Exception {
private List<String> getUrls(List<Tuple2<LiveRequestMapping, SpringProcessLiveData>> mappingMethods) throws Exception {
List<String> urls = new ArrayList<>();
for (int i = 0; i < mappingMethods.size(); i++) {
Tuple2<RequestMapping, SpringBootApp> mappingMethod = mappingMethods.get(i);
SpringBootApp app = mappingMethod.getT2();
String contextPath = app.getContextPath();
Tuple2<LiveRequestMapping, SpringProcessLiveData> mappingMethod = mappingMethods.get(i);
SpringProcessLiveData liveData = mappingMethod.getT2();
String contextPath = liveData.getContextPath();
String urlScheme = app.getUrlScheme();
String port = app.getPort();
String host = app.getHost();
String urlScheme = liveData.getUrlScheme();
String port = liveData.getPort();
String host = liveData.getHost();
String[] paths = mappingMethod.getT1().getSplitPath();
if (paths==null || paths.length==0) {
@@ -249,16 +249,16 @@ public class RequestMappingHoverProvider implements HoverProvider {
return urls;
}
private Hover createHoverWithContent(List<Tuple2<RequestMapping, SpringBootApp>> mappingMethods) throws Exception {
private Hover createHoverWithContent(List<Tuple2<LiveRequestMapping, SpringProcessLiveData>> mappingMethods) throws Exception {
StringBuilder contentVal = new StringBuilder();
for (int i = 0; i < mappingMethods.size(); i++) {
Tuple2<RequestMapping, SpringBootApp> mappingMethod = mappingMethods.get(i);
Tuple2<LiveRequestMapping, SpringProcessLiveData> mappingMethod = mappingMethods.get(i);
SpringBootApp app = mappingMethod.getT2();
String urlScheme = app.getUrlScheme();
String port = app.getPort();
String host = app.getHost();
SpringProcessLiveData liveData = mappingMethod.getT2();
String urlScheme = liveData.getUrlScheme();
String port = liveData.getPort();
String host = liveData.getHost();
String[] paths = mappingMethod.getT1().getSplitPath();
if (paths==null || paths.length==0) {
@@ -268,7 +268,7 @@ public class RequestMappingHoverProvider implements HoverProvider {
//So we'll pretend this is the same as path="" as that gives a working link.
paths = new String[] {""};
}
String contextPath = app.getContextPath();
String contextPath = liveData.getContextPath();
List<Renderable> renderableUrls = Arrays.stream(paths).flatMap(path -> {
String url = UrlUtil.createUrl(urlScheme, host, port, path, contextPath);
return Stream.of(Renderables.link(url, url), Renderables.lineBreak());
@@ -278,7 +278,7 @@ public class RequestMappingHoverProvider implements HoverProvider {
Renderable urlRenderables = Renderables.concat(renderableUrls);
Renderable processSection = Renderables.concat(
urlRenderables,
Renderables.mdBlob(LiveHoverUtils.niceAppName(app))
Renderables.mdBlob(LiveHoverUtils.niceAppName(liveData))
);
if (i < mappingMethods.size() - 1) {

View File

@@ -13,8 +13,8 @@ package org.springframework.ide.vscode.boot.java.utils;
import java.util.ArrayList;
import java.util.List;
import org.springframework.ide.vscode.boot.java.livehover.v2.LiveBean;
import org.springframework.ide.vscode.commons.boot.app.cli.SpringBootApp;
import org.springframework.ide.vscode.commons.boot.app.cli.livebean.LiveBean;
/**
* @author Martin Lippert

View File

@@ -17,9 +17,9 @@ import java.util.List;
import java.util.Set;
import org.apache.commons.lang3.StringUtils;
import org.springframework.ide.vscode.boot.java.livehover.v2.LiveBean;
import org.springframework.ide.vscode.boot.java.livehover.v2.LiveBeansModel;
import org.springframework.ide.vscode.commons.boot.app.cli.SpringBootApp;
import org.springframework.ide.vscode.commons.boot.app.cli.livebean.LiveBean;
import org.springframework.ide.vscode.commons.boot.app.cli.livebean.LiveBeansModel;
/**
* @author Martin Lippert
@@ -59,17 +59,17 @@ public class ChangeHistory {
public Change checkForUpdates() {
Change result = null;
LiveBeansModel currentBeans = this.associatedProcess.getBeans();
if (!currentBeans.isEmpty()) {
if (lastBeans == null) {
lastBeans = currentBeans;
}
else if (lastBeans != null && currentBeans != null) {
result = calculateBeansDiff(lastBeans, currentBeans, result);
lastBeans = currentBeans;
}
}
// LiveBeansModel currentBeans = this.associatedProcess.getBeans();
// if (!currentBeans.isEmpty()) {
//
// if (lastBeans == null) {
// lastBeans = currentBeans;
// }
// else if (lastBeans != null && currentBeans != null) {
// result = calculateBeansDiff(lastBeans, currentBeans, result);
// lastBeans = currentBeans;
// }
// }
return result;
}

View File

@@ -0,0 +1,63 @@
/*******************************************************************************
* 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
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.boot.java.utils;
import java.lang.reflect.Field;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.FileASTRequestor;
import org.eclipse.jdt.internal.compiler.env.INameEnvironment;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author Martin Lippert
*/
public class NameEnvironmentAwareASTRequestor extends FileASTRequestor {
private static final Logger log = LoggerFactory.getLogger(NameEnvironmentAwareASTRequestor.class);
private FileASTRequestor realOne;
private INameEnvironment nameEnvironment;
public NameEnvironmentAwareASTRequestor(FileASTRequestor realOne) {
this.realOne = realOne;
}
@Override
public void acceptAST(String sourceFilePath, CompilationUnit cu) {
if (nameEnvironment == null) {
extractNameEnvironment();
}
realOne.acceptAST(sourceFilePath, cu);
}
public INameEnvironment getNameEnvironment() {
return nameEnvironment;
}
private void extractNameEnvironment() {
try {
Field declaredField = FileASTRequestor.class.getDeclaredField("compilationUnitResolver");
declaredField.setAccessible(true);
Object compilationUnitResolver = declaredField.get(this);
Object lookupEnvironment = compilationUnitResolver.getClass().getField("lookupEnvironment")
.get(compilationUnitResolver);
nameEnvironment = (INameEnvironment) lookupEnvironment.getClass()
.getField("nameEnvironment").get(lookupEnvironment);
} catch (Exception e) {
log.error(" could not identify name environment when scanning for symbols in Java code - " + e.getMessage());
}
}
}

View File

@@ -18,10 +18,11 @@ import org.eclipse.jdt.core.dom.Annotation;
import org.eclipse.jdt.core.dom.IAnnotationBinding;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ide.vscode.boot.java.Annotations;
import org.springframework.ide.vscode.boot.java.handlers.AbstractSymbolProvider;
import org.springframework.ide.vscode.boot.java.handlers.EnhancedSymbolInformation;
import org.springframework.ide.vscode.commons.util.Log;
import org.springframework.ide.vscode.commons.util.text.TextDocument;
/**
@@ -29,6 +30,8 @@ import org.springframework.ide.vscode.commons.util.text.TextDocument;
*/
public class RestrictedDefaultSymbolProvider extends AbstractSymbolProvider {
private static final Logger log = LoggerFactory.getLogger(RestrictedDefaultSymbolProvider.class);
@Override
protected void addSymbolsPass1(Annotation node, ITypeBinding typeBinding,
Collection<ITypeBinding> metaAnnotations, SpringIndexerJavaContext context, TextDocument doc) {
@@ -39,7 +42,7 @@ public class RestrictedDefaultSymbolProvider extends AbstractSymbolProvider {
EnhancedSymbolInformation enhancedSymbol = new EnhancedSymbolInformation(DefaultSymbolProvider.provideDefaultSymbol(node, doc), null);
context.getGeneratedSymbols().add(new CachedSymbol(context.getDocURI(), context.getLastModified(), enhancedSymbol));
} catch (Exception e) {
Log.log(e);
log.warn(e.getMessage());
}
}
}

View File

@@ -37,8 +37,8 @@ import org.springframework.ide.vscode.boot.java.BootJavaLanguageServerComponents
import org.springframework.ide.vscode.boot.java.handlers.RunningAppMatcher;
import org.springframework.ide.vscode.boot.java.handlers.RunningAppProvider;
import org.springframework.ide.vscode.boot.java.links.SourceLinks;
import org.springframework.ide.vscode.boot.java.livehover.v2.LiveBean;
import org.springframework.ide.vscode.commons.boot.app.cli.SpringBootApp;
import org.springframework.ide.vscode.commons.boot.app.cli.livebean.LiveBean;
import org.springframework.ide.vscode.commons.java.IJavaProject;
import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder;
import org.springframework.ide.vscode.commons.languageserver.java.ProjectObserver;

View File

@@ -29,6 +29,7 @@ import org.slf4j.LoggerFactory;
import org.springframework.ide.vscode.boot.java.handlers.BootJavaHoverProvider;
import org.springframework.ide.vscode.boot.java.handlers.RunningAppMatcher;
import org.springframework.ide.vscode.boot.java.handlers.RunningAppProvider;
import org.springframework.ide.vscode.boot.java.livehover.v2.SpringProcessLiveDataProvider;
import org.springframework.ide.vscode.commons.boot.app.cli.SpringBootApp;
import org.springframework.ide.vscode.commons.java.IJavaProject;
import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder;
@@ -57,17 +58,14 @@ public class SpringLiveHoverWatchdog {
private ScheduledThreadPoolExecutor timer;
private JavaProjectFinder projectFinder;
private final Map<String, AtomicReference<IJavaProject>> watchedDocs;
public SpringLiveHoverWatchdog(
SimpleLanguageServer server,
BootJavaHoverProvider hoverProvider,
RunningAppProvider runningAppProvider,
JavaProjectFinder projectFinder,
ProjectObserver projectChanges,
Duration pollingInterval
) {
Duration pollingInterval) {
this.POLLING_INTERVAL_MILLISECONDS = pollingInterval == null ? DEFAULT_INTERVAL.toMillis() : pollingInterval.toMillis();
this.server = server;
this.hoverProvider = hoverProvider;
@@ -176,9 +174,6 @@ public class SpringLiveHoverWatchdog {
SpringBootApp[] matchingApps = RunningAppMatcher.getAllMatchingApps(cachedApps, project).toArray(new SpringBootApp[0]);
update(docURI, project, matchingApps);
}
this.hoverProvider.setRunningSpringApps(cachedApps);
} catch (Exception e) {
logger.error("", e);
}
@@ -193,7 +188,7 @@ public class SpringLiveHoverWatchdog {
if (hasCurrentRunningBootApps) {
TextDocument doc = this.server.getTextDocumentService().get(docURI);
if (doc != null) {
CodeLens[] infos = this.hoverProvider.getLiveHoverHints(doc, project, runningBootApps);
CodeLens[] infos = this.hoverProvider.getLiveHoverHints(doc, project);
publishLiveHints(docURI, infos);
}
}

View File

@@ -34,9 +34,9 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.boot.java.livehover.v2.LiveProperties;
import org.springframework.ide.vscode.boot.java.livehover.v2.LiveProperty;
import org.springframework.ide.vscode.boot.java.livehover.v2.SpringProcessLiveData;
import org.springframework.ide.vscode.commons.java.IJavaProject;
import org.springframework.ide.vscode.commons.util.BadLocationException;
import org.springframework.ide.vscode.commons.util.Renderable;
@@ -60,7 +60,7 @@ public class ValueHoverProvider implements HoverProvider {
@Override
public Hover provideHover(ASTNode node, Annotation annotation, ITypeBinding type, int offset, TextDocument doc,
IJavaProject project, SpringBootApp[] runningApps) {
IJavaProject project, SpringProcessLiveData[] processLiveData) {
try {
ASTNode foundNode = NodeFinder.perform(node, offset, 0);
@@ -68,7 +68,7 @@ public class ValueHoverProvider implements HoverProvider {
if (exactNode != null) {
return provideHover(exactNode.toString(), offset - exactNode.getStartPosition(),
exactNode.getStartPosition(), doc, runningApps);
exactNode.getStartPosition(), doc, processLiveData);
}
} catch (Exception e) {
logger.error("Error while generating live hovers for @Value", e);
@@ -96,7 +96,7 @@ public class ValueHoverProvider implements HoverProvider {
return null;
}
private Hover provideHover(String value, int offset, int nodeStartOffset, TextDocument doc, SpringBootApp[] runningApps) {
private Hover provideHover(String value, int offset, int nodeStartOffset, TextDocument doc, SpringProcessLiveData[] processLiveData) {
try {
LocalRange range = parsePropertyOnHoverOffset(value, offset);
@@ -104,11 +104,11 @@ public class ValueHoverProvider implements HoverProvider {
String propertyKey = value.substring(range.getStart(), range.getEnd());
if (propertyKey != null) {
Map<SpringBootApp, LiveProperties> allProperties = getPropertiesFromProcesses(runningApps);
Map<SpringProcessLiveData, LiveProperties> allProperties = getPropertiesFromProcesses(processLiveData);
StringBuilder hover = new StringBuilder();
for (SpringBootApp app : allProperties.keySet()) {
for (SpringProcessLiveData app : allProperties.keySet()) {
LiveProperties properties = allProperties.get(app);
List<LiveProperty> foundProperties = properties.getProperties(propertyKey);
if (foundProperties != null) {
@@ -150,10 +150,10 @@ public class ValueHoverProvider implements HoverProvider {
*
* @param doc
* @param node
* @param runningApps
* @param processLiveData
* @return
*/
private List<CodeLens> provideHighlightHints(TextDocument doc, StringLiteral node, SpringBootApp[] runningApps) {
private List<CodeLens> provideHighlightHints(TextDocument doc, StringLiteral node, SpringProcessLiveData[] processLiveData) {
ASTNode exactNode = getExactNode(node);
if (exactNode != null) {
@@ -183,7 +183,7 @@ public class ValueHoverProvider implements HoverProvider {
String parsedProp = entry.getKey();
List<LocalRange> propRanges = entry.getValue();
List<LiveProperty> matchingLiveProperties = findMatchingLiveProperties(runningApps, parsedProp);
List<LiveProperty> matchingLiveProperties = findMatchingLiveProperties(processLiveData, parsedProp);
if (matchingLiveProperties != null && !matchingLiveProperties.isEmpty()) {
propRanges.stream().forEach(propRange -> {
@@ -206,11 +206,11 @@ public class ValueHoverProvider implements HoverProvider {
return ImmutableList.of();
}
private List<LiveProperty> findMatchingLiveProperties(SpringBootApp[] runningApps, String propFromValue) {
Map<SpringBootApp, LiveProperties> allProperties = getPropertiesFromProcesses(runningApps);
private List<LiveProperty> findMatchingLiveProperties(SpringProcessLiveData[] processLiveData, String propFromValue) {
Map<SpringProcessLiveData, LiveProperties> allProperties = getPropertiesFromProcesses(processLiveData);
for (SpringBootApp app : allProperties.keySet()) {
LiveProperties properties = allProperties.get(app);
for (SpringProcessLiveData liveData : allProperties.keySet()) {
LiveProperties properties = allProperties.get(liveData);
List<LiveProperty> matchingLiveProperties = properties.getProperties(propFromValue);
if (matchingLiveProperties != null && !matchingLiveProperties.isEmpty()) {
return matchingLiveProperties;
@@ -219,14 +219,14 @@ public class ValueHoverProvider implements HoverProvider {
return null;
}
public Map<SpringBootApp, LiveProperties> getPropertiesFromProcesses(SpringBootApp[] runningApps) {
Map<SpringBootApp, LiveProperties> result = new HashMap<>();
public Map<SpringProcessLiveData, LiveProperties> getPropertiesFromProcesses(SpringProcessLiveData[] processLiveData) {
Map<SpringProcessLiveData, LiveProperties> result = new HashMap<>();
try {
for (SpringBootApp app : runningApps) {
LiveProperties liveProperties = app.getLiveProperties();
for (SpringProcessLiveData liveData : processLiveData) {
LiveProperties liveProperties = liveData.getLiveProperties();
if (liveProperties != null) {
result.put(app, liveProperties);
result.put(liveData, liveProperties);
}
}
}
@@ -361,19 +361,19 @@ public class ValueHoverProvider implements HoverProvider {
@Override
public Hover provideHover(ASTNode node, TypeDeclaration typeDeclaration, ITypeBinding type, int offset,
TextDocument doc, IJavaProject project, SpringBootApp[] runningApps) {
TextDocument doc, IJavaProject project, SpringProcessLiveData[] processLiveData) {
return null;
}
@Override
public Collection<CodeLens> getLiveHintCodeLenses(IJavaProject project, Annotation annotation, TextDocument doc, SpringBootApp[] runningApps) {
public Collection<CodeLens> getLiveHintCodeLenses(IJavaProject project, Annotation annotation, TextDocument doc, SpringProcessLiveData[] processLiveData) {
// Show highlight hints for properties in @Value that have live information
List<CodeLens> lenses = new ArrayList<>();
annotation.accept(new ASTVisitor() {
@Override
public boolean visit(StringLiteral node) {
List<CodeLens> provideHighlightHints = provideHighlightHints(doc, node, runningApps);
List<CodeLens> provideHighlightHints = provideHighlightHints(doc, node, processLiveData);
lenses.addAll(provideHighlightHints);
return super.visit(node);
}

View File

@@ -23,8 +23,8 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Import;
import org.springframework.ide.vscode.boot.bootiful.BootLanguageServerTest;
import org.springframework.ide.vscode.boot.bootiful.HoverTestConf;
import org.springframework.ide.vscode.commons.boot.app.cli.livebean.LiveBean;
import org.springframework.ide.vscode.commons.boot.app.cli.livebean.LiveBeansModel;
import org.springframework.ide.vscode.boot.java.livehover.v2.LiveBean;
import org.springframework.ide.vscode.boot.java.livehover.v2.LiveBeansModel;
import org.springframework.ide.vscode.commons.maven.java.MavenJavaProject;
import org.springframework.ide.vscode.commons.util.text.LanguageId;
import org.springframework.ide.vscode.languageserver.testharness.Editor;

View File

@@ -22,8 +22,8 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Import;
import org.springframework.ide.vscode.boot.bootiful.BootLanguageServerTest;
import org.springframework.ide.vscode.boot.bootiful.HoverTestConf;
import org.springframework.ide.vscode.commons.boot.app.cli.livebean.LiveBean;
import org.springframework.ide.vscode.commons.boot.app.cli.livebean.LiveBeansModel;
import org.springframework.ide.vscode.boot.java.livehover.v2.LiveBean;
import org.springframework.ide.vscode.boot.java.livehover.v2.LiveBeansModel;
import org.springframework.ide.vscode.commons.maven.java.MavenJavaProject;
import org.springframework.ide.vscode.commons.util.text.LanguageId;
import org.springframework.ide.vscode.languageserver.testharness.Editor;

View File

@@ -17,8 +17,8 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Import;
import org.springframework.ide.vscode.boot.bootiful.BootLanguageServerTest;
import org.springframework.ide.vscode.boot.bootiful.HoverTestConf;
import org.springframework.ide.vscode.commons.boot.app.cli.livebean.LiveBean;
import org.springframework.ide.vscode.commons.boot.app.cli.livebean.LiveBeansModel;
import org.springframework.ide.vscode.boot.java.livehover.v2.LiveBean;
import org.springframework.ide.vscode.boot.java.livehover.v2.LiveBeansModel;
import org.springframework.ide.vscode.commons.maven.java.MavenJavaProject;
import org.springframework.ide.vscode.commons.util.text.LanguageId;
import org.springframework.ide.vscode.languageserver.testharness.Editor;

View File

@@ -19,8 +19,8 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Import;
import org.springframework.ide.vscode.boot.bootiful.BootLanguageServerTest;
import org.springframework.ide.vscode.boot.bootiful.HoverTestConf;
import org.springframework.ide.vscode.commons.boot.app.cli.livebean.LiveBean;
import org.springframework.ide.vscode.commons.boot.app.cli.livebean.LiveBeansModel;
import org.springframework.ide.vscode.boot.java.livehover.v2.LiveBean;
import org.springframework.ide.vscode.boot.java.livehover.v2.LiveBeansModel;
import org.springframework.ide.vscode.commons.maven.java.MavenJavaProject;
import org.springframework.ide.vscode.commons.util.text.LanguageId;
import org.springframework.ide.vscode.languageserver.testharness.Editor;

View File

@@ -2,11 +2,11 @@ package org.springframework.ide.vscode.project.harness;
import java.util.Set;
import org.springframework.ide.vscode.commons.boot.app.cli.requestmappings.RequestMapping;
import org.springframework.ide.vscode.boot.java.livehover.v2.LiveRequestMapping;
import com.google.common.collect.ImmutableSet;
public class MockRequestMapping implements RequestMapping {
public class MockRequestMapping implements LiveRequestMapping {
private String[] paths = {};
private String className;

View File

@@ -21,14 +21,14 @@ import java.util.Properties;
import org.mockito.Mockito;
import org.springframework.ide.vscode.boot.java.handlers.RunningAppProvider;
import org.springframework.ide.vscode.boot.java.livehover.v2.LiveBeansModel;
import org.springframework.ide.vscode.boot.java.livehover.v2.LiveConditional;
import org.springframework.ide.vscode.boot.java.livehover.v2.LiveProperties;
import org.springframework.ide.vscode.boot.java.livehover.v2.LivePropertiesJsonParser;
import org.springframework.ide.vscode.boot.java.livehover.v2.LiveRequestMapping;
import org.springframework.ide.vscode.commons.boot.app.cli.ContextPath;
import org.springframework.ide.vscode.commons.boot.app.cli.LiveConditional;
import org.springframework.ide.vscode.commons.boot.app.cli.LocalSpringBootApp;
import org.springframework.ide.vscode.commons.boot.app.cli.SpringBootApp;
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.liveproperties.LivePropertiesJsonParser;
import org.springframework.ide.vscode.commons.boot.app.cli.requestmappings.RequestMapping;
import org.springframework.ide.vscode.commons.util.ExceptionUtil;
import com.google.common.collect.ImmutableList;
@@ -126,12 +126,12 @@ public class MockRunningAppProvider {
}
public MockAppBuilder requestMappingsJson(String mappings) throws Exception {
Collection<RequestMapping> requestMappings = LocalSpringBootApp.parseRequestMappingsJson(mappings, "1.x");
Collection<LiveRequestMapping> requestMappings = LocalSpringBootApp.parseRequestMappingsJson(mappings, "1.x");
when(app.getRequestMappings()).thenReturn(requestMappings);
return this;
}
public MockAppBuilder requestMappings(Collection<RequestMapping> rms) throws Exception {
public MockAppBuilder requestMappings(Collection<LiveRequestMapping> rms) throws Exception {
when(app.getRequestMappings()).thenReturn(rms);
return this;
}