live hover mechanics, v2
This commit is contained in:
@@ -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) {
|
||||
|
||||
@@ -0,0 +1,133 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2017, 2018 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;
|
||||
|
||||
/**
|
||||
* @author Martin Lippert
|
||||
* @author Kris De Volder
|
||||
*/
|
||||
public class LiveBean {
|
||||
|
||||
private final String id;
|
||||
private final String[] aliases;
|
||||
private final String scope;
|
||||
private final String type;
|
||||
private final String resource;
|
||||
private final String[] dependencies;
|
||||
|
||||
public static class Builder {
|
||||
private String id;
|
||||
private String[] aliases = {};
|
||||
private String scope;
|
||||
private String type;
|
||||
private String resource;
|
||||
private String[] dependencies = {};
|
||||
|
||||
public LiveBean build() {
|
||||
return new LiveBean(id, aliases, scope, type, resource, dependencies);
|
||||
}
|
||||
|
||||
public Builder id(String id) {
|
||||
this.id = id;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder type(String type) {
|
||||
this.type = type;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder dependencies(String... deps) {
|
||||
this.dependencies = deps;
|
||||
return this;
|
||||
}
|
||||
|
||||
private Builder resource(String resource) {
|
||||
this.resource = resource;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder fileResource(String path) {
|
||||
return resource("file ["+path+"]");
|
||||
}
|
||||
|
||||
public Builder classpathResource(String path) {
|
||||
return resource("class path resource ["+path+"]");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected LiveBean(String id, String[] aliases, String scope, String type, String resource, String[] dependencies) {
|
||||
super();
|
||||
this.id = id;
|
||||
this.aliases = aliases;
|
||||
this.scope = scope;
|
||||
this.type = type;
|
||||
this.resource = "null".equals(resource) ? null : resource;
|
||||
this.dependencies = dependencies;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String[] getAliases() {
|
||||
return aliases;
|
||||
}
|
||||
|
||||
public String getScope() {
|
||||
return scope;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public String getType(boolean stripCGLib) {
|
||||
String type = this.type;
|
||||
|
||||
if (type != null) {
|
||||
if (stripCGLib) {
|
||||
int chop = type.indexOf("$$EnhancerBySpringCGLIB$$");
|
||||
if (chop >= 0) {
|
||||
type = type.substring(0, chop);
|
||||
}
|
||||
|
||||
chop = type.indexOf("$$Lambda$");
|
||||
if (chop >= 0) {
|
||||
type = type.substring(0, chop);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
public String getResource() {
|
||||
return resource;
|
||||
}
|
||||
|
||||
public String[] getDependencies() {
|
||||
return dependencies;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "LiveBean [id=" + id + ", type=" + type + ", dependencies=" + Arrays.toString(dependencies) + "]";
|
||||
}
|
||||
|
||||
public static Builder builder() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,240 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2017 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.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.ide.vscode.commons.util.StringUtil;
|
||||
|
||||
import com.google.common.collect.ImmutableListMultimap;
|
||||
|
||||
/**
|
||||
* @author Martin Lippert
|
||||
* @author Kris De Volder
|
||||
*/
|
||||
public class LiveBeansModel {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(LiveBeansModel.class);
|
||||
|
||||
private static final String[] NO_STRINGS = new String[] {};
|
||||
|
||||
public static class Builder {
|
||||
private final ImmutableListMultimap.Builder<String, LiveBean> beansViaName = ImmutableListMultimap.builder();
|
||||
private final ImmutableListMultimap.Builder<String, LiveBean> beansViaType = ImmutableListMultimap.builder();
|
||||
private final ImmutableListMultimap.Builder<String, LiveBean> beansViaDependency = ImmutableListMultimap.builder();
|
||||
|
||||
public LiveBeansModel build() {
|
||||
return new LiveBeansModel(beansViaName.build(), beansViaType.build(), beansViaDependency.build());
|
||||
}
|
||||
|
||||
public Builder add(LiveBean bean) {
|
||||
String type = bean.getType();
|
||||
if (type != null) {
|
||||
beansViaType.put(type, bean);
|
||||
}
|
||||
|
||||
String name = bean.getId();
|
||||
if (name != null) {
|
||||
beansViaName.put(name, bean);
|
||||
}
|
||||
|
||||
String[] deps = bean.getDependencies();
|
||||
if (deps!=null) {
|
||||
for (String dep : deps) {
|
||||
beansViaDependency.put(dep, bean);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static LiveBeansModel.Builder builder() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
interface Parser {
|
||||
LiveBeansModel parse(String json) throws Exception;
|
||||
}
|
||||
|
||||
private static class Boot15Parser implements Parser {
|
||||
@Override
|
||||
public LiveBeansModel parse(String json) throws Exception {
|
||||
Builder model = LiveBeansModel.builder();
|
||||
JSONArray mainArray = new JSONArray(json);
|
||||
for (int i = 0; i < mainArray.length(); i++) {
|
||||
JSONObject appContext = mainArray.getJSONObject(i);
|
||||
if (appContext == null) continue;
|
||||
|
||||
JSONArray beansArray = appContext.optJSONArray("beans");
|
||||
if (beansArray == null) continue;
|
||||
|
||||
for (int j = 0; j < beansArray.length(); j++) {
|
||||
JSONObject beanObject = beansArray.getJSONObject(j);
|
||||
if (beanObject == null) continue;
|
||||
|
||||
LiveBean bean = parseBean(beanObject);
|
||||
if (bean != null) {
|
||||
model.add(bean);
|
||||
}
|
||||
}
|
||||
}
|
||||
return model.build();
|
||||
}
|
||||
private LiveBean parseBean(JSONObject beansJSON) {
|
||||
String id = beansJSON.optString("bean");
|
||||
String type = beansJSON.optString("type");
|
||||
String scope = beansJSON.optString("scope");
|
||||
String resource = beansJSON.optString("resource");
|
||||
|
||||
JSONArray aliasesJSON = beansJSON.optJSONArray("aliases");
|
||||
String[] aliases;
|
||||
if (aliasesJSON!=null) {
|
||||
aliases = new String[aliasesJSON.length()];
|
||||
for (int i = 0; i < aliasesJSON.length(); i++) {
|
||||
aliases[i] = aliasesJSON.optString(i);
|
||||
}
|
||||
} else {
|
||||
aliases = NO_STRINGS;
|
||||
}
|
||||
|
||||
JSONArray dependenciesJSON = beansJSON.getJSONArray("dependencies");
|
||||
String[] dependencies = new String[dependenciesJSON.length()];
|
||||
for (int i = 0; i < dependenciesJSON.length(); i++) {
|
||||
dependencies[i] = dependenciesJSON.optString(i);
|
||||
}
|
||||
|
||||
return new LiveBean(id, aliases, scope, type, resource, dependencies);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class Boot20Parser implements Parser {
|
||||
|
||||
@Override
|
||||
public LiveBeansModel parse(String json) throws Exception {
|
||||
Builder model = LiveBeansModel.builder();
|
||||
JSONObject mainObject = new JSONObject(json);
|
||||
|
||||
mainObject = mainObject.getJSONObject("contexts");
|
||||
for (String contextId : mainObject.keySet()) {
|
||||
JSONObject contextObject = mainObject.getJSONObject(contextId);
|
||||
JSONObject beansObject = contextObject.getJSONObject("beans");
|
||||
for (String id : beansObject.keySet()) {
|
||||
JSONObject beanObject = beansObject.getJSONObject(id);
|
||||
LiveBean bean = parseBean(id, contextId, beanObject);
|
||||
if (bean!=null) {
|
||||
model.add(bean);
|
||||
}
|
||||
}
|
||||
}
|
||||
return model.build();
|
||||
}
|
||||
|
||||
private LiveBean parseBean(String id, String contextId, JSONObject beansJSON) {
|
||||
String type = beansJSON.optString("type");
|
||||
String scope = beansJSON.optString("scope");
|
||||
String resource = beansJSON.optString("resource");
|
||||
|
||||
JSONArray aliasesJSON = beansJSON.optJSONArray("aliases");
|
||||
String[] aliases;
|
||||
if (aliasesJSON==null) {
|
||||
aliases = NO_STRINGS;
|
||||
} else {
|
||||
aliases = new String[aliasesJSON.length()];
|
||||
for (int i = 0; i < aliasesJSON.length(); i++) {
|
||||
aliases[i] = aliasesJSON.optString(i);
|
||||
}
|
||||
}
|
||||
|
||||
JSONArray dependenciesJSON = beansJSON.optJSONArray("dependencies");
|
||||
String[] dependencies;
|
||||
if (dependenciesJSON==null) {
|
||||
dependencies = NO_STRINGS;
|
||||
} else {
|
||||
dependencies = new String[dependenciesJSON.length()];
|
||||
for (int i = 0; i < dependenciesJSON.length(); i++) {
|
||||
dependencies[i] = dependenciesJSON.optString(i);
|
||||
}
|
||||
}
|
||||
|
||||
return new LiveBean(id, aliases, scope, type, resource, dependencies);
|
||||
}
|
||||
}
|
||||
|
||||
private static final Parser[] PARSERS = {
|
||||
new Boot15Parser(),
|
||||
new Boot20Parser(),
|
||||
};
|
||||
|
||||
public static LiveBeansModel parse(String json) {
|
||||
List<Exception> exceptions = new ArrayList<>(PARSERS.length);
|
||||
if (StringUtil.hasText(json)) {
|
||||
for (Parser parser : PARSERS) {
|
||||
try {
|
||||
LiveBeansModel model = parser.parse(json);
|
||||
if (model==null) {
|
||||
throw new NullPointerException("Parser returned a null model (it should not!)");
|
||||
}
|
||||
return model; //good!
|
||||
} catch (Exception e) {
|
||||
exceptions.add(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
//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.warn(e.getMessage());
|
||||
}
|
||||
return LiveBeansModel.builder().build(); // always return at least an empty model.
|
||||
}
|
||||
|
||||
private final ImmutableListMultimap<String, LiveBean> beansViaType;
|
||||
private final ImmutableListMultimap<String, LiveBean> beansViaName;
|
||||
private final ImmutableListMultimap<String, LiveBean> beansViaDependency;
|
||||
|
||||
protected LiveBeansModel(
|
||||
ImmutableListMultimap<String, LiveBean> beansViaName,
|
||||
ImmutableListMultimap<String, LiveBean> beansViaType,
|
||||
ImmutableListMultimap<String, LiveBean> beansViaDependency) {
|
||||
this.beansViaName = beansViaName;
|
||||
this.beansViaType = beansViaType;
|
||||
this.beansViaDependency = beansViaDependency;
|
||||
}
|
||||
|
||||
public List<LiveBean> getBeansOfType(String fullyQualifiedType) {
|
||||
return beansViaType.get(fullyQualifiedType);
|
||||
}
|
||||
|
||||
public List<LiveBean> getBeansOfName(String beanName) {
|
||||
return beansViaName.get(beanName);
|
||||
}
|
||||
|
||||
public List<LiveBean> getBeansDependingOn(String beanName) {
|
||||
return beansViaDependency.get(beanName);
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return beansViaName.isEmpty(); //Assumes every bean has a name.
|
||||
}
|
||||
|
||||
public Set<String> getBeanNames() {
|
||||
return beansViaName.keySet();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2017 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;
|
||||
|
||||
public class LiveConditional {
|
||||
|
||||
private String condition;
|
||||
private String message;
|
||||
private String processId;
|
||||
private String processName;
|
||||
private String typeInfo;
|
||||
|
||||
public LiveConditional() {
|
||||
|
||||
}
|
||||
|
||||
public String getCondition() {
|
||||
return condition;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public String getProcessId() {
|
||||
return processId;
|
||||
}
|
||||
|
||||
public String getProcessName() {
|
||||
return processName;
|
||||
}
|
||||
|
||||
public String getTypeInfo() {
|
||||
return typeInfo;
|
||||
}
|
||||
|
||||
public static class LiveConditionalBuilder {
|
||||
|
||||
private LiveConditional conditional = new LiveConditional();
|
||||
|
||||
public LiveConditionalBuilder condition(String condition) {
|
||||
conditional.condition = condition;
|
||||
return this;
|
||||
}
|
||||
|
||||
public LiveConditionalBuilder message(String message) {
|
||||
conditional.message = message;
|
||||
return this;
|
||||
}
|
||||
|
||||
public LiveConditionalBuilder processId(String processId) {
|
||||
conditional.processId = processId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public LiveConditionalBuilder processName(String processName) {
|
||||
conditional.processName = processName;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Type information for which a conditional is applied to.
|
||||
* <p/>
|
||||
*
|
||||
* Example:
|
||||
* <p/>
|
||||
* For this class:
|
||||
* <p/>
|
||||
* "@ConditionalOnClass(name="java.lang.String2")
|
||||
* public class MyConditionalComponent {
|
||||
* }"
|
||||
* <p/>
|
||||
* This is the "real" autoconfig JSON:
|
||||
* <p/>
|
||||
* "negativeMatches": { "MyConditionalComponent": { "notMatched": [ {
|
||||
* "condition": "OnClassCondition", "message": "@ConditionalOnClass did not find
|
||||
* required class 'java.lang.String2'" } ], "matched": [] }
|
||||
* <p/>
|
||||
* In this example, "MyConditionalComponent" information in the JSON indicates the type where the conditional is being applied to.
|
||||
* <p/>
|
||||
* Type info can also contain method information if a conditional annotation is applied to a method. Example: MyConditionalComponent#myBean)
|
||||
* @param typeInfo
|
||||
* @return
|
||||
*/
|
||||
public LiveConditionalBuilder typeInfo(String typeInfo) {
|
||||
conditional.typeInfo = typeInfo;
|
||||
return this;
|
||||
}
|
||||
|
||||
public LiveConditional build() {
|
||||
return conditional;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static LiveConditionalBuilder builder() {
|
||||
return new LiveConditionalBuilder();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2017 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.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.ide.vscode.commons.util.StringUtil;
|
||||
|
||||
/**
|
||||
* Parsers @ConditionalOn annotations from a spring boot running app's
|
||||
* autoconfig report. An example of a conditional that is parsed from a running
|
||||
* app's autoconfig report would be:
|
||||
*
|
||||
* {"TraceRepositoryAutoConfiguration#traceRepository":[{"condition":"OnBeanCondition","message":"@ConditionalOnMissingBean
|
||||
* (types: org.springframework.boot.actuate.trace.TraceRepository;
|
||||
* SearchStrategy: all) did not find any beans"}]
|
||||
*
|
||||
*/
|
||||
public class LiveConditionalParser {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(LiveConditionalParser.class);
|
||||
|
||||
private final String autoConfigRecord;
|
||||
private final String appProcessName;
|
||||
private final String appProcessId;
|
||||
|
||||
public LiveConditionalParser(String autoConfigRecord, String appProcessId, String appProcessName) {
|
||||
this.autoConfigRecord = autoConfigRecord;
|
||||
this.appProcessId = appProcessId;
|
||||
this.appProcessName = appProcessName;
|
||||
}
|
||||
|
||||
public LiveConditional[] parse() {
|
||||
try {
|
||||
List<LiveConditional> allConditionals = new ArrayList<>();
|
||||
if (StringUtil.hasText(autoConfigRecord)) {
|
||||
JSONObject autoConfigReport = new JSONObject(autoConfigRecord);
|
||||
if (autoConfigReport.has("contexts")) {
|
||||
//more recently the report is nested inside the 'application' context.
|
||||
autoConfigReport = autoConfigReport.getJSONObject("contexts").getJSONObject("application");
|
||||
}
|
||||
for (LiveConditional c : getConditionalsFromPositiveMatches(autoConfigReport)) {
|
||||
allConditionals.add(c);
|
||||
}
|
||||
for (LiveConditional c : getConditionalsFromNegativeMatches(autoConfigReport)) {
|
||||
allConditionals.add(c);
|
||||
}
|
||||
}
|
||||
if (!allConditionals.isEmpty()) {
|
||||
return (LiveConditional[]) allConditionals.toArray(new LiveConditional[allConditionals.size()]);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn(e.getMessage());
|
||||
}
|
||||
return new LiveConditional[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the "positiveMatches" element in the autoconfig report JSON that contains conditional information.
|
||||
*/
|
||||
private Optional<JSONObject> getPositiveMatchesJson(JSONObject autoConfigReport) {
|
||||
return Optional.ofNullable(autoConfigReport.optJSONObject("positiveMatches"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the "negativeMatches" element in the autoconfig report JSON that contains conditional information.
|
||||
*/
|
||||
private Optional<JSONObject> getNegativeMatchesJson(JSONObject autoConfigReport) {
|
||||
return Optional.ofNullable(autoConfigReport.optJSONObject("negativeMatches"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches all the conditionals listed in the the "positiveMatches" element in the autoconfig report.
|
||||
*
|
||||
*/
|
||||
private List<LiveConditional> getConditionalsFromPositiveMatches(JSONObject autoConfigReport) {
|
||||
List<LiveConditional> conditions = new ArrayList<>();
|
||||
getPositiveMatchesJson(autoConfigReport).ifPresent((matches) -> {
|
||||
for (String typeInfo : matches.keySet()) {
|
||||
// The positive match key contains the bean method information where conditional
|
||||
// was applied to
|
||||
Object val = matches.get(typeInfo);
|
||||
if (val instanceof JSONArray) {
|
||||
JSONArray contentList = (JSONArray) val;
|
||||
parseConditionalsFromContentList(conditions, typeInfo, contentList);
|
||||
}
|
||||
}
|
||||
});
|
||||
return conditions;
|
||||
}
|
||||
|
||||
private List<LiveConditional> getConditionalsFromNegativeMatches(JSONObject autoConfigReport) {
|
||||
List<LiveConditional> conditions = new ArrayList<>();
|
||||
// The JSON structure being parsed is:
|
||||
// "negativeMatches": {
|
||||
// "MyConditionalComponent": {
|
||||
// "notMatched": [
|
||||
// {
|
||||
// "condition": "OnClassCondition",
|
||||
// "message": "@ConditionalOnClass did not find required class 'java.lang.String2'"
|
||||
// }
|
||||
// ],
|
||||
// "matched": []
|
||||
// }
|
||||
getNegativeMatchesJson(autoConfigReport).ifPresent((matches) -> {
|
||||
// The key in the "matches" JSON contains the live type information where the conditional was applied to
|
||||
for (String typeInfo : matches.keySet()) {
|
||||
// The positive match key contains the bean method information where conditional
|
||||
// was applied to
|
||||
Object val = matches.get(typeInfo);
|
||||
if (val instanceof JSONObject) {
|
||||
JSONObject negativeMatches = (JSONObject) val;
|
||||
negativeMatches.keySet().stream().forEach((key) -> {
|
||||
JSONArray contentList = (JSONArray) negativeMatches.get(key);
|
||||
parseConditionalsFromContentList(conditions, typeInfo, contentList);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
return conditions;
|
||||
}
|
||||
|
||||
private void parseConditionalsFromContentList(List<LiveConditional> conditionals, String typeInfo,
|
||||
JSONArray contentList) {
|
||||
for (Object content : contentList) {
|
||||
if (content instanceof JSONObject) {
|
||||
JSONObject conditionalJson = (JSONObject) content;
|
||||
String condition = (String) conditionalJson.get("condition");
|
||||
String message = (String) conditionalJson.get("message");
|
||||
// We care about the message itself as it contains the actual annotation as well
|
||||
// as the reason it matched
|
||||
if (StringUtil.hasText(message)) {
|
||||
LiveConditional conditional = LiveConditional.builder().processId(appProcessId)
|
||||
.processName(appProcessName).condition(condition).message(message).typeInfo(typeInfo)
|
||||
.build();
|
||||
conditionals.add(conditional);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static LiveConditional[] parse(String autoConfigRecord, String appProcessId,
|
||||
String appProcessName) {
|
||||
return new LiveConditionalParser(autoConfigRecord, appProcessId, appProcessName).parse();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*******************************************************************************
|
||||
* 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.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
public class LiveProperties {
|
||||
|
||||
private final List<LivePropertySource> sources;
|
||||
|
||||
public LiveProperties(List<LivePropertySource> sources) {
|
||||
this.sources = sources != null ? ImmutableList.copyOf(sources) : ImmutableList.of();
|
||||
}
|
||||
|
||||
public List<LiveProperty> getProperties(String propertyName) {
|
||||
List<LiveProperty> foundProperties = new ArrayList<>();
|
||||
for (LivePropertySource source : sources) {
|
||||
LiveProperty property = source.getProperty(propertyName);
|
||||
if (property != null) {
|
||||
foundProperties.add(property);
|
||||
}
|
||||
}
|
||||
return foundProperties;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2019 Pivotal, Inc.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* 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.List;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
public abstract class LivePropertiesJsonParser {
|
||||
|
||||
/**
|
||||
* Parse live properties from the given JSON input. Returns null if no live
|
||||
* properties could be parsed
|
||||
*
|
||||
* @param jsonInput
|
||||
* @return live properties if parsed, or null otherwise
|
||||
* @throws Exception
|
||||
*/
|
||||
public LiveProperties parse(String jsonInput) throws Exception {
|
||||
|
||||
JSONObject envObj = toJson(jsonInput);
|
||||
|
||||
List<LivePropertySource> propertySources = readProperties(envObj);
|
||||
if (propertySources != null && !propertySources.isEmpty()) {
|
||||
return new LiveProperties(propertySources);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
protected JSONObject toJson(String json) throws JSONException {
|
||||
return new JSONObject(json);
|
||||
}
|
||||
|
||||
protected abstract List<LivePropertySource> readProperties(JSONObject envObj) throws Exception;
|
||||
|
||||
public static LiveProperties parseProperties(String jsonInput) throws Exception {
|
||||
LivePropertiesJsonParser2x boot2x = new LivePropertiesJsonParser2x();
|
||||
LiveProperties properties = boot2x.parse(jsonInput);
|
||||
if (properties == null) {
|
||||
LivePropertiesJsonParser1x boot1x = new LivePropertiesJsonParser1x();
|
||||
properties = boot1x.parse(jsonInput);
|
||||
}
|
||||
return properties;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import org.json.JSONObject;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
public class LivePropertiesJsonParser1x extends LivePropertiesJsonParser {
|
||||
|
||||
public LivePropertiesJsonParser1x() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param envObj
|
||||
* @return non-null PropertySources. Content in the PropertySources may be empty
|
||||
* if no sources are found
|
||||
* @throws Exception
|
||||
*/
|
||||
@Override
|
||||
protected List<LivePropertySource> readProperties(JSONObject allSourcesJson) throws Exception {
|
||||
ImmutableList.Builder<LivePropertySource> allSources = ImmutableList.builder();
|
||||
|
||||
if (allSourcesJson != null) {
|
||||
Iterator<?> keys = allSourcesJson.keys();
|
||||
if (keys != null) {
|
||||
while (keys.hasNext()) {
|
||||
Object key = keys.next();
|
||||
if (key instanceof String) {
|
||||
String sourceName = (String) key;
|
||||
// Skip profiles
|
||||
if (!"profiles".equals(sourceName)) {
|
||||
Object sourceObj = allSourcesJson.opt(sourceName);
|
||||
ImmutableList.Builder<LiveProperty> parsedProps = ImmutableList.builder();
|
||||
|
||||
if (sourceObj instanceof JSONObject) {
|
||||
JSONObject source = (JSONObject) sourceObj;
|
||||
Iterator<?> propKeys = source.keys();
|
||||
if (propKeys != null) {
|
||||
while (propKeys.hasNext()) {
|
||||
Object propObjKey = propKeys.next();
|
||||
if (propObjKey instanceof String) {
|
||||
String propName = (String) propObjKey;
|
||||
Object valObj = source.optString(propName);
|
||||
if (valObj instanceof String) {
|
||||
String value = (String) valObj;
|
||||
LiveProperty property = LiveProperty
|
||||
.builder() //
|
||||
.source(sourceName) //
|
||||
.property(propName) //
|
||||
.value(value) //
|
||||
.build();
|
||||
parsedProps.add(property);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
LivePropertySource propertySource = new LivePropertySource(sourceName, parsedProps.build());
|
||||
allSources.add(propertySource);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return allSources.build();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2019 Pivotal, Inc.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* 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.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
public class LivePropertiesJsonParser2x extends LivePropertiesJsonParser {
|
||||
|
||||
public LivePropertiesJsonParser2x() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param envObj
|
||||
* @return non-null PropertySources. Content in the PropertySources may be empty
|
||||
* if no sources are found
|
||||
* @throws Exception
|
||||
*/
|
||||
@Override
|
||||
protected List<LivePropertySource> readProperties(JSONObject envObj) throws Exception {
|
||||
ImmutableList.Builder<LivePropertySource> allSources = ImmutableList.builder();
|
||||
|
||||
Object sourcesObj = envObj.opt("propertySources");
|
||||
|
||||
if (sourcesObj instanceof JSONArray) {
|
||||
JSONArray props = (JSONArray) sourcesObj;
|
||||
for (int i = 0; i < props.length(); i++) {
|
||||
Object object = props.opt(i);
|
||||
if (object instanceof JSONObject) {
|
||||
JSONObject propObj = (JSONObject) object;
|
||||
String sourceName = propObj.optString("name");
|
||||
if (sourceName != null) {
|
||||
Object opt2 = propObj.opt("properties");
|
||||
List<LiveProperty> properties = parseProperties(sourceName, opt2);
|
||||
|
||||
LivePropertySource propertySource = new LivePropertySource(sourceName, properties);
|
||||
|
||||
allSources.add(propertySource);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return allSources.build();
|
||||
}
|
||||
|
||||
private List<LiveProperty> parseProperties(String sourceName, Object opt2) {
|
||||
List<LiveProperty> properties = new ArrayList<>();
|
||||
|
||||
if (opt2 instanceof JSONObject) {
|
||||
JSONObject jsonObj = (JSONObject) opt2;
|
||||
Iterator<?> keys = jsonObj.keys();
|
||||
if (keys != null) {
|
||||
while (keys.hasNext()) {
|
||||
Object key = keys.next();
|
||||
if (key instanceof String) {
|
||||
String propKey = (String) key;
|
||||
Object propContentObj = jsonObj.opt(propKey);
|
||||
if (propContentObj != null) {
|
||||
String value = getValue(propContentObj);
|
||||
LiveProperty property = LiveProperty.builder() //
|
||||
.source(sourceName) //
|
||||
.property(propKey) //
|
||||
.value(value) //
|
||||
.build();
|
||||
properties.add(property);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return properties;
|
||||
}
|
||||
|
||||
private String getValue(Object propContentObj) {
|
||||
if (propContentObj instanceof JSONObject) {
|
||||
JSONObject jsonObj = (JSONObject) propContentObj;
|
||||
return jsonObj.optString("value");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2019 Pivotal, Inc.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* https://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.livehover.v2;
|
||||
|
||||
public class LiveProperty {
|
||||
|
||||
private String source;
|
||||
private String property;
|
||||
private String value;
|
||||
|
||||
public String getSource() {
|
||||
return source;
|
||||
}
|
||||
|
||||
public String getProperty() {
|
||||
return property;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public static class LivePropertyBuilder {
|
||||
|
||||
private LiveProperty liveProperty = new LiveProperty();
|
||||
|
||||
public LivePropertyBuilder source(String source) {
|
||||
liveProperty.source = source;
|
||||
return this;
|
||||
}
|
||||
|
||||
public LivePropertyBuilder property(String property) {
|
||||
liveProperty.property = property;
|
||||
return this;
|
||||
}
|
||||
|
||||
public LivePropertyBuilder value(String value) {
|
||||
liveProperty.value = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
public LiveProperty build() {
|
||||
return liveProperty;
|
||||
}
|
||||
}
|
||||
|
||||
public static LivePropertyBuilder builder() {
|
||||
return new LivePropertyBuilder();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*******************************************************************************
|
||||
* 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.List;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
public class LivePropertySource {
|
||||
|
||||
private final List<LiveProperty> properties;
|
||||
private final String sourceName;
|
||||
|
||||
public LivePropertySource(String sourceName, List<LiveProperty> properties) {
|
||||
this.sourceName = sourceName;
|
||||
this.properties = properties != null ? ImmutableList.copyOf(properties) : ImmutableList.of();
|
||||
}
|
||||
|
||||
public String getSourceName() {
|
||||
return this.sourceName;
|
||||
}
|
||||
|
||||
public LiveProperty getProperty(String propertyName) {
|
||||
for (LiveProperty liveProperty : properties) {
|
||||
if (liveProperty.getProperty().equals(propertyName)) {
|
||||
return liveProperty;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2017 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.Set;
|
||||
|
||||
public interface LiveRequestMapping {
|
||||
// String getPath(); commented... because... not used??
|
||||
String[] getSplitPath();
|
||||
String getFullyQualifiedClassName();
|
||||
String getMethodName();
|
||||
String[] getMethodParameters();
|
||||
String getMethodString();
|
||||
Set<String> getRequestMethods();
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2018 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.Collections;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.ide.vscode.commons.java.parser.JLRMethodParser;
|
||||
import org.springframework.ide.vscode.commons.java.parser.JLRMethodParser.JLRMethod;
|
||||
|
||||
import com.google.common.base.Supplier;
|
||||
import com.google.common.base.Suppliers;
|
||||
|
||||
public abstract class LiveRequestMappingAbstractImpl implements LiveRequestMapping {
|
||||
|
||||
private static final Pattern REQUEST_METHODS_PATTERN = Pattern.compile(".*methods=\\[(.*)\\].*");
|
||||
|
||||
final private Supplier<JLRMethod> methodDataSupplier;
|
||||
final private Supplier<Set<String>> requestMethodsSupplier;
|
||||
final private Supplier<String[]> pathsSuplier;
|
||||
|
||||
public LiveRequestMappingAbstractImpl() {
|
||||
this.requestMethodsSupplier = Suppliers.memoize(() -> parseRequestMethods());
|
||||
this.methodDataSupplier = Suppliers.memoize(() -> JLRMethodParser.parse(getMethodString()));
|
||||
this.pathsSuplier = Suppliers.memoize(() -> computePaths());
|
||||
}
|
||||
|
||||
protected Set<String> parseRequestMethods() {
|
||||
Matcher matcher = REQUEST_METHODS_PATTERN.matcher(getPredicateString());
|
||||
if (matcher.matches()) {
|
||||
return Arrays.stream(matcher.group(1).split("\\s*,\\s*")).collect(Collectors.toSet());
|
||||
}
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getRequestMethods() {
|
||||
return requestMethodsSupplier.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final String getFullyQualifiedClassName() {
|
||||
JLRMethod m = getMethodData();
|
||||
if (m!=null) {
|
||||
return m.getFQClassName();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
protected JLRMethod getMethodData() {
|
||||
return methodDataSupplier.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final String getMethodName() {
|
||||
JLRMethod m = getMethodData();
|
||||
if (m!=null) {
|
||||
return m.getMethodName();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getMethodParameters() {
|
||||
return getMethodData().getParameters();
|
||||
}
|
||||
|
||||
protected String[] computePaths() {
|
||||
//Two cases we know about:
|
||||
// 1: the 'predicate' is a path string
|
||||
// 2: the 'predicate' looks something like:
|
||||
// "{[/actuator/health],methods=[GET],produces=[application/vnd.spring-boot.actuator.v2+json || application/json]}
|
||||
String predicate = getPredicateString();
|
||||
if (predicate.startsWith("{[")) {
|
||||
//An almost json string. Unfortunately not really json so we can't
|
||||
//use org.json or jackson Mapper to properly parse this.
|
||||
int start = 2; //right after first '['
|
||||
int end = predicate.indexOf(']');
|
||||
if (end>=2) {
|
||||
String pathString = predicate.substring(start, end);
|
||||
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 splitPaths(predicate);
|
||||
}
|
||||
|
||||
protected abstract String getPredicateString();
|
||||
|
||||
@Override
|
||||
public String[] getSplitPath() {
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2017 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 org.json.JSONObject;
|
||||
|
||||
import com.google.common.base.Objects;
|
||||
|
||||
public class LiveRequestMappingBoot1xRequestMapping extends LiveRequestMappingAbstractImpl {
|
||||
|
||||
/*
|
||||
There are two styles of entries:
|
||||
|
||||
1) key is a 'path' String. May contain patters like "**"
|
||||
"/** /favicon.ico":{
|
||||
"bean":"faviconHandlerMapping"
|
||||
}
|
||||
|
||||
2) key is a 'almost json' String
|
||||
"{[/bye],methods=[],params=[],headers=[],consumes=[],produces=[],custom=[]}":{
|
||||
"bean":"requestMappingHandlerMapping",
|
||||
"method":"public java.lang.String demo.MyController.bye()"
|
||||
}
|
||||
*/
|
||||
|
||||
private JSONObject beanInfo;
|
||||
private String pathKey;
|
||||
|
||||
public LiveRequestMappingBoot1xRequestMapping(String pathKey, JSONObject beanInfo) {
|
||||
this.pathKey = pathKey;
|
||||
this.beanInfo = beanInfo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return pathKey.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
LiveRequestMappingBoot1xRequestMapping other = (LiveRequestMappingBoot1xRequestMapping) obj;
|
||||
return Objects.equal(this.pathKey, other.pathKey)
|
||||
&& Objects.equal(this.getMethodString(), other.getMethodString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMethodString() {
|
||||
return beanInfo.optString("method");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getPredicateString() {
|
||||
return pathKey;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,187 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2018 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.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
import org.objectweb.asm.Type;
|
||||
|
||||
public class LiveRequestMappingBoot2xDispatcherServletMapping implements LiveRequestMapping {
|
||||
|
||||
/*
|
||||
|
||||
Example entry:
|
||||
|
||||
{
|
||||
"handler": "public java.lang.String com.example.demo.RestApi.aha(java.lang.String)",
|
||||
"predicate": "{[/genericHello || /aha],methods=[GET]}",
|
||||
"details": {
|
||||
"requestMappingConditions": {
|
||||
"headers": [],
|
||||
"methods": [
|
||||
"GET"
|
||||
],
|
||||
"patterns": [
|
||||
"/genericHello",
|
||||
"/aha"
|
||||
],
|
||||
"produces": [],
|
||||
"params": [],
|
||||
"consumes": []
|
||||
},
|
||||
"handlerMethod": {
|
||||
"name": "aha",
|
||||
"className": "com.example.demo.RestApi",
|
||||
"descriptor": "(Ljava/lang/String;)Ljava/lang/String;"
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
private JSONObject data;
|
||||
|
||||
public LiveRequestMappingBoot2xDispatcherServletMapping(JSONObject data) {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
private JSONObject getDetails() {
|
||||
return data.optJSONObject("details");
|
||||
}
|
||||
|
||||
private String getPredicate() {
|
||||
return data.optString("predicate");
|
||||
}
|
||||
|
||||
private JSONObject getHandlerMethod() {
|
||||
JSONObject details = getDetails();
|
||||
if (details != null) {
|
||||
if (details.has("handlerMethod")) {
|
||||
return details.getJSONObject("handlerMethod");
|
||||
} else if (details.has("handlerFunction")) {
|
||||
//TODO: handler function for the Router bean
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private JSONObject getHandlerFunction() {
|
||||
JSONObject details = getDetails();
|
||||
if (details != null) {
|
||||
if (details.has("handlerFunction")) {
|
||||
return details.getJSONObject("handlerFunction");
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private JSONObject getRequestMappingConditions() {
|
||||
JSONObject details = getDetails();
|
||||
return details == null ? null : details.optJSONObject("requestMappingConditions");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMethodString() {
|
||||
return data.optString("handler");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFullyQualifiedClassName() {
|
||||
JSONObject handlerMethod = getHandlerMethod();
|
||||
if (handlerMethod != null) {
|
||||
return handlerMethod.getString("className");
|
||||
}
|
||||
JSONObject handlerFunction = getHandlerFunction();
|
||||
if (handlerFunction != null) {
|
||||
return handlerFunction.getString("className");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMethodName() {
|
||||
JSONObject handlerMethod = getHandlerMethod();
|
||||
return handlerMethod == null ? null : handlerMethod.getString("name");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getMethodParameters() {
|
||||
JSONObject handlerMethod = getHandlerMethod();
|
||||
if (handlerMethod != null) {
|
||||
Type type = Type.getMethodType(handlerMethod.getString("descriptor"));
|
||||
Type[] argsTypes = type.getArgumentTypes();
|
||||
String[] parameterTypes = new String[argsTypes.length];
|
||||
for (int i = 0; i < argsTypes.length; i++) {
|
||||
parameterTypes[i] = argsTypes[i].getClassName();
|
||||
}
|
||||
return parameterTypes;
|
||||
}
|
||||
return new String[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getSplitPath() {
|
||||
JSONObject rmConditions = getRequestMappingConditions();
|
||||
if (rmConditions == null) {
|
||||
String predicate = getPredicate();
|
||||
if (predicate != null) {
|
||||
// Predicate is and/or string expression: ((GET && /hello) && Accept: [text/plain])
|
||||
String[] tokens = predicate.split("\\w*(&&|\\|\\|)\\w*");
|
||||
List<String> splitPaths = new ArrayList<>(tokens.length);
|
||||
for (String t : tokens) {
|
||||
// Remove leading `(`, trailing `)`
|
||||
String token = removeLeadingAndTrailingParenthises(t);
|
||||
if (!token.isEmpty() && token.charAt(0) == '/') {
|
||||
splitPaths.add(token);
|
||||
}
|
||||
}
|
||||
return splitPaths.toArray(new String[splitPaths.size()]);
|
||||
}
|
||||
} else {
|
||||
JSONArray jsonArray = rmConditions.getJSONArray("patterns");
|
||||
String[] paths = new String[jsonArray.length()];
|
||||
for (int i = 0; i < jsonArray.length(); i++) {
|
||||
paths[i] = jsonArray.getString(i);
|
||||
}
|
||||
return paths;
|
||||
}
|
||||
return new String[0];
|
||||
}
|
||||
|
||||
private static String removeLeadingAndTrailingParenthises(String s) {
|
||||
int start = 0;
|
||||
int end = s.length();
|
||||
for(; start < s.length() && (s.charAt(start) == '(' || Character.isWhitespace(s.charAt(start))); start++);
|
||||
for(; end > start && (s.charAt(end - 1) == ')' || Character.isWhitespace(s.charAt(end - 1))); end--);
|
||||
return start <= end ? s.substring(start, end) : "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getRequestMethods() {
|
||||
JSONObject rmConditions = getRequestMappingConditions();
|
||||
if (rmConditions == null) {
|
||||
return Collections.emptySet();
|
||||
} else {
|
||||
JSONArray jsonArray = rmConditions.getJSONArray("methods");
|
||||
Set<String> methods = new HashSet<>();
|
||||
for (int i = 0; i < jsonArray.length(); i++) {
|
||||
methods.add(jsonArray.getString(i));
|
||||
}
|
||||
return methods;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/*******************************************************************************
|
||||
* 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
|
||||
* 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.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
|
||||
public class LiveRequestMappingBoot2xParser {
|
||||
|
||||
public static LiveRequestMapping[] parse(JSONObject obj) {
|
||||
obj = obj.getJSONObject("contexts");
|
||||
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
|
||||
// know from Boot 1.x but in slighly different form. We only parse that stuff for now.
|
||||
JSONObject mappings = obj
|
||||
.getJSONObject(contextId)
|
||||
.getJSONObject("mappings");
|
||||
JSONArray rmArray = null;
|
||||
if (mappings.has("dispatcherServlets") && mappings.getJSONObject("dispatcherServlets").has("dispatcherServlet")) {
|
||||
// Regular Web starter endpoints RMs JMX beans format
|
||||
rmArray = mappings.getJSONObject("dispatcherServlets").getJSONArray("dispatcherServlet");
|
||||
} else if (mappings.has("dispatcherHandlers") && mappings.getJSONObject("dispatcherHandlers").has("webHandler")) {
|
||||
// WebFlux endpoints RMs JMX bean format
|
||||
rmArray = mappings.getJSONObject("dispatcherHandlers").getJSONArray("webHandler");
|
||||
}
|
||||
if (rmArray != null) {
|
||||
for (Object e : rmArray) {
|
||||
JSONObject rm = (JSONObject)e;
|
||||
result.add(new LiveRequestMappingBoot2xDispatcherServletMapping(rm));
|
||||
}
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/*******************************************************************************
|
||||
* 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 SpringProcessLiveDataChangeEvent {
|
||||
|
||||
private final SpringProcessLiveData[] updatedLiveData;
|
||||
|
||||
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