From 7c4c3f50f575801f437b0146c83d13cc0dd3586e Mon Sep 17 00:00:00 2001 From: Martin Lippert Date: Mon, 2 Oct 2017 16:30:33 +0200 Subject: [PATCH] PT #151428541: first steps to gather live hover hint data from running apps --- .../boot/java/BootJavaLanguageServer.java | 48 ++++++- .../java/handlers/BootJavaHoverProvider.java | 126 ++++++++++++++++-- .../boot/java/handlers/HoverProvider.java | 6 +- .../RequestMappingHoverProvider.java | 48 ++++--- .../java/utils/SpringLiveHoverWatchdog.java | 117 ++++++++++++++++ .../boot/java/value/ValueHoverProvider.java | 40 +++--- .../commons/boot/app/cli/SpringBootApp.java | 26 ++++ 7 files changed, 348 insertions(+), 63 deletions(-) create mode 100644 headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringLiveHoverWatchdog.java diff --git a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/BootJavaLanguageServer.java b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/BootJavaLanguageServer.java index a225fc6ec..f9b7ba00f 100644 --- a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/BootJavaLanguageServer.java +++ b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/BootJavaLanguageServer.java @@ -12,9 +12,11 @@ package org.springframework.ide.vscode.boot.java; import java.util.HashMap; import java.util.Map; +import java.util.concurrent.CompletableFuture; import org.eclipse.lsp4j.CompletionItemKind; -import org.eclipse.lsp4j.InitializedParams; +import org.eclipse.lsp4j.InitializeParams; +import org.eclipse.lsp4j.InitializeResult; import org.springframework.ide.vscode.boot.java.beans.BeansSymbolProvider; import org.springframework.ide.vscode.boot.java.beans.ComponentSymbolProvider; import org.springframework.ide.vscode.boot.java.handlers.BootJavaCodeLensEngine; @@ -32,9 +34,10 @@ import org.springframework.ide.vscode.boot.java.requestmapping.RequestMappingHov import org.springframework.ide.vscode.boot.java.requestmapping.RequestMappingSymbolProvider; import org.springframework.ide.vscode.boot.java.scope.ScopeCompletionProcessor; import org.springframework.ide.vscode.boot.java.snippets.JavaSnippet; -import org.springframework.ide.vscode.boot.java.snippets.JavaSnippetManager; 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.SpringIndexer; +import org.springframework.ide.vscode.boot.java.utils.SpringLiveHoverWatchdog; import org.springframework.ide.vscode.boot.java.value.ValueCompletionProcessor; import org.springframework.ide.vscode.boot.java.value.ValueHoverProvider; import org.springframework.ide.vscode.boot.java.value.ValuePropertyReferencesProvider; @@ -47,7 +50,6 @@ import org.springframework.ide.vscode.commons.languageserver.java.DefaultJavaPro import org.springframework.ide.vscode.commons.languageserver.java.IJavaProjectFinderStrategy; import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder; import org.springframework.ide.vscode.commons.languageserver.reconcile.IReconcileEngine; -import org.springframework.ide.vscode.commons.languageserver.util.HoverHandler; import org.springframework.ide.vscode.commons.languageserver.util.ReferencesHandler; import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer; import org.springframework.ide.vscode.commons.languageserver.util.SimpleTextDocumentService; @@ -66,6 +68,8 @@ import com.google.common.collect.ImmutableList; */ public class BootJavaLanguageServer extends SimpleLanguageServer { + public static final String LANGUAGE_SERVER_PROCESS_PROPERTY = "spring-boot-language-server"; + public static final JavaProjectFinder DEFAULT_PROJECT_FINDER = new DefaultJavaProjectFinder(new IJavaProjectFinderStrategy[] { new MavenProjectFinderStrategy(MavenCore.getDefault()), new GradleProjectFinderStrategy(GradleCore.getDefault()), @@ -74,9 +78,13 @@ public class BootJavaLanguageServer extends SimpleLanguageServer { private final VscodeCompletionEngineAdapter completionEngine; private final SpringIndexer indexer; + private final SpringLiveHoverWatchdog liveHoverWatchdog; public BootJavaLanguageServer(JavaProjectFinder javaProjectFinder, SpringPropertyIndexProvider indexProvider) { super("vscode-boot-java"); + + System.setProperty(LANGUAGE_SERVER_PROCESS_PROPERTY, LANGUAGE_SERVER_PROCESS_PROPERTY); + SimpleWorkspaceService workspaceService = getWorkspaceService(); SimpleTextDocumentService documents = getTextDocumentService(); @@ -92,9 +100,16 @@ public class BootJavaLanguageServer extends SimpleLanguageServer { documents.onCompletion(completionEngine::getCompletions); documents.onCompletionResolve(completionEngine::resolveCompletion); - HoverHandler hoverInfoProvider = createHoverHandler(javaProjectFinder); + BootJavaHoverProvider hoverInfoProvider = createHoverHandler(javaProjectFinder); documents.onHover(hoverInfoProvider); + liveHoverWatchdog = new SpringLiveHoverWatchdog(this, hoverInfoProvider); + documents.onDidChangeContent(params -> { + TextDocument doc = params.getDocument(); + liveHoverWatchdog.watchDocument(doc.getUri()); + liveHoverWatchdog.update(doc.getUri()); + }); + ReferencesHandler referencesHandler = createReferenceHandler(this, javaProjectFinder); documents.onReferences(referencesHandler); @@ -112,9 +127,28 @@ public class BootJavaLanguageServer extends SimpleLanguageServer { } @Override - public void initialized(InitializedParams params) { - super.initialized(params); + public CompletableFuture initialize(InitializeParams params) { + CompletableFuture result = super.initialize(params); + this.indexer.initialize(this.getWorkspaceRoot()); + this.liveHoverWatchdog.start(); + + return result; + } + + @Override + public void initialized() { + // TODO: due to a missing message from lsp4e this "initialized" is not called in the LSP4E case + // if this gets fixed, the code should move here (from "initialize" above) + +// this.indexer.initialize(this.getWorkspaceRoot()); +// this.liveHoverWatchdog.start(); + } + + @Override + public CompletableFuture shutdown() { + liveHoverWatchdog.shutdown(); + return super.shutdown(); } protected ICompletionEngine createCompletionEngine(JavaProjectFinder javaProjectFinder, SpringPropertyIndexProvider indexProvider) { @@ -184,7 +218,7 @@ public class BootJavaLanguageServer extends SimpleLanguageServer { return new BootJavaCompletionEngine(javaProjectFinder, providers, snippetManager); } - protected HoverHandler createHoverHandler(JavaProjectFinder javaProjectFinder) { + protected BootJavaHoverProvider createHoverHandler(JavaProjectFinder javaProjectFinder) { HashMap providers = new HashMap<>(); providers.put(org.springframework.ide.vscode.boot.java.value.Constants.SPRING_VALUE, new ValueHoverProvider()); providers.put(org.springframework.ide.vscode.boot.java.requestmapping.Constants.SPRING_REQUEST_MAPPING, new RequestMappingHoverProvider()); diff --git a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/BootJavaHoverProvider.java b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/BootJavaHoverProvider.java index f1af4f491..d719a838f 100644 --- a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/BootJavaHoverProvider.java +++ b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/BootJavaHoverProvider.java @@ -11,6 +11,8 @@ package org.springframework.ide.vscode.boot.java.handlers; import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; import java.util.Map; import java.util.concurrent.CompletableFuture; import java.util.stream.Stream; @@ -19,18 +21,26 @@ import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.core.dom.AST; import org.eclipse.jdt.core.dom.ASTNode; import org.eclipse.jdt.core.dom.ASTParser; +import org.eclipse.jdt.core.dom.ASTVisitor; import org.eclipse.jdt.core.dom.Annotation; import org.eclipse.jdt.core.dom.CompilationUnit; import org.eclipse.jdt.core.dom.ITypeBinding; +import org.eclipse.jdt.core.dom.MarkerAnnotation; import org.eclipse.jdt.core.dom.NodeFinder; +import org.eclipse.jdt.core.dom.NormalAnnotation; +import org.eclipse.jdt.core.dom.SingleMemberAnnotation; import org.eclipse.lsp4j.Hover; +import org.eclipse.lsp4j.Range; import org.eclipse.lsp4j.TextDocumentPositionParams; +import org.springframework.ide.vscode.boot.java.BootJavaLanguageServer; +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.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.SimpleLanguageServer; import org.springframework.ide.vscode.commons.languageserver.util.SimpleTextDocumentService; +import org.springframework.ide.vscode.commons.util.BadLocationException; import org.springframework.ide.vscode.commons.util.text.IDocument; import org.springframework.ide.vscode.commons.util.text.TextDocument; @@ -68,7 +78,89 @@ public class BootJavaHoverProvider implements HoverHandler { return SimpleTextDocumentService.NO_HOVER; } + public Range[] getLiveHoverHints(final TextDocument document, final SpringBootApp[] runningBootApps) { + List result = new ArrayList<>(); + try { + IJavaProject project = getProject(document); + CompilationUnit cu = parse(document, project); + + cu.accept(new ASTVisitor() { + @Override + public boolean visit(SingleMemberAnnotation node) { + try { + extractLiveHints(node, document, runningBootApps, result); + } + catch (Exception e) { + e.printStackTrace(); + } + + return super.visit(node); + } + + @Override + public boolean visit(NormalAnnotation node) { + try { + extractLiveHints(node, document, runningBootApps, result); + } + catch (Exception e) { + e.printStackTrace(); + } + + return super.visit(node); + } + + @Override + public boolean visit(MarkerAnnotation node) { + try { + extractLiveHints(node, document, runningBootApps, result); + } + catch (Exception e) { + e.printStackTrace(); + } + + return super.visit(node); + } + }); + } + catch (Exception e) { + e.printStackTrace(); + } + + return result.toArray(new Range[result.size()]); + } + + protected void extractLiveHints(Annotation annotation, TextDocument document, SpringBootApp[] runningApps, List result) { + ITypeBinding type = annotation.resolveTypeBinding(); + if (type != null) { + String qualifiedName = type.getQualifiedName(); + if (qualifiedName != null) { + HoverProvider provider = this.hoverProviders.get(qualifiedName); + if (provider != null) { + Range range = provider.getLiveHoverHint(annotation, document, runningApps); + if (range != null) { + result.add(range); + } + } + } + } + } + private CompletableFuture provideHover(TextDocument document, int offset) throws Exception { + IJavaProject project = getProject(document); + CompilationUnit cu = parse(document, project); + + ASTNode node = NodeFinder.perform(cu, offset, 0); + + if (node != null) { + System.out.println("AST node found: " + node.getClass().getName()); + return provideHoverForAnnotation(node, offset, document, project); + } + + return null; + } + + private CompilationUnit parse(TextDocument document, IJavaProject project) + throws Exception, BadLocationException { ASTParser parser = ASTParser.newParser(AST.JLS8); Map options = JavaCore.getOptions(); JavaCore.setComplianceOptions(JavaCore.VERSION_1_8, options); @@ -78,7 +170,7 @@ public class BootJavaHoverProvider implements HoverHandler { parser.setBindingsRecovery(true); parser.setResolveBindings(true); - String[] classpathEntries = getClasspathEntries(document); + String[] classpathEntries = getClasspathEntries(project); String[] sourceEntries = new String[] {}; parser.setEnvironment(classpathEntries, sourceEntries, null, true); @@ -88,17 +180,10 @@ public class BootJavaHoverProvider implements HoverHandler { parser.setSource(document.get(0, document.getLength()).toCharArray()); CompilationUnit cu = (CompilationUnit) parser.createAST(null); - ASTNode node = NodeFinder.perform(cu, offset, 0); - - if (node != null) { - System.out.println("AST node found: " + node.getClass().getName()); - return provideHoverForAnnotation(node, offset, document); - } - - return null; + return cu; } - private CompletableFuture provideHoverForAnnotation(ASTNode node, int offset, TextDocument doc) { + private CompletableFuture provideHoverForAnnotation(ASTNode node, int offset, TextDocument doc, IJavaProject project) { Annotation annotation = null; while (node != null && !(node instanceof Annotation)) { @@ -113,7 +198,8 @@ public class BootJavaHoverProvider implements HoverHandler { if (qualifiedName != null) { HoverProvider provider = this.hoverProviders.get(qualifiedName); if (provider != null) { - return provider.provideHover(node, annotation, type, offset, doc); + SpringBootApp[] runningApps = getRunningSpringApps(project); + return provider.provideHover(node, annotation, type, offset, doc, runningApps); } } } @@ -122,8 +208,11 @@ public class BootJavaHoverProvider implements HoverHandler { return null; } - private String[] getClasspathEntries(IDocument doc) throws Exception { - IJavaProject project = this.projectFinder.find(doc); + private IJavaProject getProject(IDocument doc) throws Exception { + return this.projectFinder.find(doc); + } + + private String[] getClasspathEntries(IJavaProject project) throws Exception { IClasspath classpath = project.getClasspath(); Stream classpathEntries = classpath.getClasspathEntries(); return classpathEntries @@ -131,4 +220,15 @@ public class BootJavaHoverProvider implements HoverHandler { .map(path -> path.toAbsolutePath().toString()).toArray(String[]::new); } + private SpringBootApp[] getRunningSpringApps(IJavaProject project) { + try { + return SpringBootApp.getAllRunningSpringApps().values().stream() + .filter((app) -> !app.containsSystemProperty(BootJavaLanguageServer.LANGUAGE_SERVER_PROCESS_PROPERTY)) + .toArray(SpringBootApp[]::new); + } catch (Exception e) { + e.printStackTrace(); + return new SpringBootApp[0]; + } + } + } diff --git a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/HoverProvider.java b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/HoverProvider.java index 69df36e53..2b40349b0 100644 --- a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/HoverProvider.java +++ b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/HoverProvider.java @@ -16,6 +16,8 @@ import org.eclipse.jdt.core.dom.ASTNode; import org.eclipse.jdt.core.dom.Annotation; import org.eclipse.jdt.core.dom.ITypeBinding; import org.eclipse.lsp4j.Hover; +import org.eclipse.lsp4j.Range; +import org.springframework.ide.vscode.commons.boot.app.cli.SpringBootApp; import org.springframework.ide.vscode.commons.util.text.TextDocument; /** @@ -23,7 +25,7 @@ import org.springframework.ide.vscode.commons.util.text.TextDocument; */ public interface HoverProvider { - CompletableFuture provideHover(ASTNode node, Annotation annotation, ITypeBinding type, int offset, - TextDocument doc); + CompletableFuture provideHover(ASTNode node, Annotation annotation, ITypeBinding type, int offset, TextDocument doc, SpringBootApp[] runningApps); + Range getLiveHoverHint(Annotation annotation, TextDocument doc, SpringBootApp[] runningApps); } diff --git a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/RequestMappingHoverProvider.java b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/RequestMappingHoverProvider.java index 31c2d3de9..73aa40c62 100644 --- a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/RequestMappingHoverProvider.java +++ b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/RequestMappingHoverProvider.java @@ -13,7 +13,6 @@ package org.springframework.ide.vscode.boot.java.requestmapping; import java.util.ArrayList; import java.util.Iterator; import java.util.List; -import java.util.Map; import java.util.concurrent.CompletableFuture; import org.eclipse.jdt.core.dom.ASTNode; @@ -31,6 +30,7 @@ import org.eclipse.lsp4j.jsonrpc.messages.Either; import org.json.JSONObject; import org.springframework.ide.vscode.boot.java.handlers.HoverProvider; import org.springframework.ide.vscode.commons.boot.app.cli.SpringBootApp; +import org.springframework.ide.vscode.commons.util.BadLocationException; import org.springframework.ide.vscode.commons.util.text.TextDocument; /** @@ -40,14 +40,32 @@ public class RequestMappingHoverProvider implements HoverProvider { @Override public CompletableFuture provideHover(ASTNode node, Annotation annotation, - ITypeBinding type, int offset, TextDocument doc) { - return provideHover(annotation, doc); + ITypeBinding type, int offset, TextDocument doc, SpringBootApp[] runningApps) { + return provideHover(annotation, doc, runningApps); } - private CompletableFuture provideHover(Annotation annotation, TextDocument doc) { + @Override + public Range getLiveHoverHint(Annotation annotation, TextDocument doc, SpringBootApp[] runningApps) { + try { + if (runningApps.length > 0) { + // TODO: this check is too simple, we need to do a lot more here + // -> check if the running app has a matching request mapping for this annotation + + Range hoverRange = doc.toRange(annotation.getStartPosition(), annotation.getLength()); + return hoverRange; + } + } + catch (BadLocationException e) { + e.printStackTrace(); + } + + return null; + } + + private CompletableFuture provideHover(Annotation annotation, TextDocument doc, SpringBootApp[] runningApps) { try { - JSONObject[] mappings = getRequestMappingsFromProcesses(); + JSONObject[] mappings = getRequestMappingsFromProcesses(runningApps); List> hoverContent = new ArrayList<>(); @@ -55,7 +73,6 @@ public class RequestMappingHoverProvider implements HoverProvider { addHoverContent(mappings[i], hoverContent, annotation); } - Range hoverRange = doc.toRange(annotation.getStartPosition(), annotation.getLength()); Hover hover = new Hover(); @@ -109,21 +126,16 @@ public class RequestMappingHoverProvider implements HoverProvider { return mappingPath != null ? key.contains(mappingPath) : false; } - public JSONObject[] getRequestMappingsFromProcesses() { + public JSONObject[] getRequestMappingsFromProcesses(SpringBootApp[] runningApps) { List result = new ArrayList<>(); try { - Map apps = SpringBootApp.getAllRunningJavaApps(); - Iterator appsIter = apps.values().iterator(); - while (appsIter.hasNext()) { - SpringBootApp app = appsIter.next(); - if (app.isSpringBootApp()) { - String mappings = app.getRequestMappings(); - if (mappings != null) { - JSONObject requestMappings = new JSONObject(mappings); - if (requestMappings != null) { - result.add(requestMappings); - } + for (SpringBootApp app : runningApps) { + String mappings = app.getRequestMappings(); + if (mappings != null) { + JSONObject requestMappings = new JSONObject(mappings); + if (requestMappings != null) { + result.add(requestMappings); } } } diff --git a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringLiveHoverWatchdog.java b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringLiveHoverWatchdog.java new file mode 100644 index 000000000..a6a55e5a0 --- /dev/null +++ b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringLiveHoverWatchdog.java @@ -0,0 +1,117 @@ +/******************************************************************************* + * 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 + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Pivotal, Inc. - initial API and implementation + *******************************************************************************/ +package org.springframework.ide.vscode.boot.java.utils; + +import java.util.Set; +import java.util.Timer; +import java.util.TimerTask; +import java.util.concurrent.ConcurrentSkipListSet; + +import org.eclipse.lsp4j.Range; +import org.springframework.ide.vscode.boot.java.BootJavaLanguageServer; +import org.springframework.ide.vscode.boot.java.handlers.BootJavaHoverProvider; +import org.springframework.ide.vscode.commons.boot.app.cli.SpringBootApp; +import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer; +import org.springframework.ide.vscode.commons.util.text.TextDocument; + +/** + * @author Martin Lippert + */ +public class SpringLiveHoverWatchdog { + + private static final int POLLING_INTERVAL_MILLISECONDS = 5000; + + private final Set watchedDocs; + private final SimpleLanguageServer server; + private final BootJavaHoverProvider hoverProvider; + + private final Timer timer; + + public SpringLiveHoverWatchdog(SimpleLanguageServer server, BootJavaHoverProvider hoverProvider) { + this.server = server; + this.hoverProvider = hoverProvider; + this.watchedDocs = new ConcurrentSkipListSet<>(); + + this.timer = new Timer(); + } + + public void start() { + TimerTask task = new TimerTask() { + @Override + public void run() { + update(); + } + }; + + timer.scheduleAtFixedRate(task, 0, POLLING_INTERVAL_MILLISECONDS); + } + + public void shutdown() { + timer.cancel(); + } + + public void watchDocument(String docURI) { + this.watchedDocs.add(docURI); + } + + public void unwatchDocument(String docURI) { + this.watchedDocs.remove(docURI); + cleanupLiveHints(docURI); + + if (watchedDocs.size() == 0) { + cleanupResources(); + } + } + + public void update(String docURI) { + try { + SpringBootApp[] runningBootApps = SpringBootApp.getAllRunningSpringApps().values().stream() + .filter((app) -> !app.containsSystemProperty(BootJavaLanguageServer.LANGUAGE_SERVER_PROCESS_PROPERTY)) + .toArray(SpringBootApp[]::new); + + TextDocument doc = this.server.getTextDocumentService().get(docURI); + if (doc != null) { + Range[] ranges = this.hoverProvider.getLiveHoverHints(doc, runningBootApps); + publishLiveHints(ranges); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + protected void update() { + if (this.watchedDocs.size() > 0) { + for (String docURI : watchedDocs) { + update(docURI); + } + } + } + + private void publishLiveHints(Range[] ranges) { + for (Range range : ranges) { + int startLine = range.getStart().getLine(); + int startChar = range.getStart().getCharacter(); + int endLine = range.getEnd().getLine(); + int endChar = range.getEnd().getCharacter(); + + System.out.println("live hover information at: " + startLine + ":" + startChar + " - " + endLine + ":" + endChar); + } + } + + private void cleanupLiveHints(String docURI) { + // TODO: send client a message to cleanup live hover diagnostics data + } + + private void cleanupResources() { + // TODO: close and cleanup open JMX connections and cached data + } + +} diff --git a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/value/ValueHoverProvider.java b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/value/ValueHoverProvider.java index 4f615be6f..66dbf6b52 100644 --- a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/value/ValueHoverProvider.java +++ b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/value/ValueHoverProvider.java @@ -13,7 +13,6 @@ package org.springframework.ide.vscode.boot.java.value; import java.util.ArrayList; import java.util.Iterator; import java.util.List; -import java.util.Map; import java.util.concurrent.CompletableFuture; import org.eclipse.jdt.core.dom.ASTNode; @@ -35,27 +34,22 @@ import org.springframework.ide.vscode.commons.util.text.TextDocument; */ public class ValueHoverProvider implements HoverProvider { - public static void register(Map hoverProviders) { - ValueHoverProvider provider = new ValueHoverProvider(); - hoverProviders.put(Constants.SPRING_VALUE, provider); - } - @Override public CompletableFuture provideHover(ASTNode node, Annotation annotation, ITypeBinding type, int offset, - TextDocument doc) { + TextDocument doc, SpringBootApp[] runningApps) { try { // case: @Value("prefix<*>") if (node instanceof StringLiteral && node.getParent() instanceof Annotation) { if (node.toString().startsWith("\"") && node.toString().endsWith("\"")) { - return provideHover(node.toString(), offset - node.getStartPosition(), node.getStartPosition(), doc); + return provideHover(node.toString(), offset - node.getStartPosition(), node.getStartPosition(), doc, runningApps); } } // case: @Value(value="prefix<*>") else if (node instanceof StringLiteral && node.getParent() instanceof MemberValuePair && "value".equals(((MemberValuePair)node.getParent()).getName().toString())) { if (node.toString().startsWith("\"") && node.toString().endsWith("\"")) { - return provideHover(node.toString(), offset - node.getStartPosition(), node.getStartPosition(), doc); + return provideHover(node.toString(), offset - node.getStartPosition(), node.getStartPosition(), doc, runningApps); } } } @@ -66,7 +60,12 @@ public class ValueHoverProvider implements HoverProvider { return null; } - private CompletableFuture provideHover(String value, int offset, int nodeStartOffset, TextDocument doc) { + @Override + public Range getLiveHoverHint(Annotation annotation, TextDocument doc, SpringBootApp[] runningApps) { + return null; + } + + private CompletableFuture provideHover(String value, int offset, int nodeStartOffset, TextDocument doc, SpringBootApp[] runningApps) { try { LocalRange range = getPropertyRange(value, offset); @@ -74,7 +73,7 @@ public class ValueHoverProvider implements HoverProvider { String propertyKey = value.substring(range.getStart(), range.getEnd()); if (propertyKey != null) { - JSONObject[] allProperties = getPropertiesFromProcesses(); + JSONObject[] allProperties = getPropertiesFromProcesses(runningApps); List> hoverContent = new ArrayList<>(); @@ -114,21 +113,16 @@ public class ValueHoverProvider implements HoverProvider { return null; } - public JSONObject[] getPropertiesFromProcesses() { + public JSONObject[] getPropertiesFromProcesses(SpringBootApp[] runningApps) { List result = new ArrayList<>(); try { - Map apps = SpringBootApp.getAllRunningJavaApps(); - Iterator appsIter = apps.values().iterator(); - while (appsIter.hasNext()) { - SpringBootApp app = appsIter.next(); - if (app.isSpringBootApp()) { - String environment = app.getEnvironment(); - if (environment != null) { - JSONObject env = new JSONObject(environment); - if (env != null) { - result.add(env); - } + for (SpringBootApp app : runningApps) { + String environment = app.getEnvironment(); + if (environment != null) { + JSONObject env = new JSONObject(environment); + if (env != null) { + result.add(env); } } } diff --git a/headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/SpringBootApp.java b/headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/SpringBootApp.java index 5ea87426e..6b7b28158 100644 --- a/headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/SpringBootApp.java +++ b/headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/SpringBootApp.java @@ -55,6 +55,22 @@ public class SpringBootApp { return result; } + /** + * @return Map that contains the boot apps, mapping the process ID -> boot app accessor object + */ + public static Map getAllRunningSpringApps() throws Exception { + Map result = new HashMap<>(); + List list = VirtualMachine.list(); + for (VirtualMachineDescriptor vmd : list) { + SpringBootApp app = new SpringBootApp(vmd); + if (app.isSpringBootApp()) { + result.put(app.getProcessID(), app); + } + } + + return result; + } + public SpringBootApp(VirtualMachineDescriptor vmd) throws Exception { this.vmd = vmd; this.vm = VirtualMachine.attach(vmd); @@ -76,6 +92,16 @@ public class SpringBootApp { return contains(cpElements, "spring-boot"); } + public boolean containsSystemProperty(Object key) { + try { + Properties props = this.vm.getSystemProperties(); + return props.containsKey(key); + } + catch (Exception e) { + return false; + } + } + public String getPort() throws Exception { String jmxConnect = this.vm.startLocalManagementAgent();