live hover mechanics, v2
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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());
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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.
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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) {
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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 {
|
||||
|
||||
@@ -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();
|
||||
@@ -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();
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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()]);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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]);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user