diff --git a/headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/AbstractSpringBootApp.java b/headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/AbstractSpringBootApp.java index 41f85ecc6..e642e8eae 100644 --- a/headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/AbstractSpringBootApp.java +++ b/headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/AbstractSpringBootApp.java @@ -42,12 +42,14 @@ import org.json.JSONArray; import org.json.JSONObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.ide.vscode.commons.boot.app.cli.livebean.LiveBeansModel; -import org.springframework.ide.vscode.commons.boot.app.cli.liveproperties.LiveProperties; -import org.springframework.ide.vscode.commons.boot.app.cli.liveproperties.LivePropertiesJsonParser; -import org.springframework.ide.vscode.commons.boot.app.cli.requestmappings.Boot1xRequestMapping; -import org.springframework.ide.vscode.commons.boot.app.cli.requestmappings.RequestMapping; -import org.springframework.ide.vscode.commons.boot.app.cli.requestmappings.RequestMappingsParser20; +import org.springframework.ide.vscode.boot.java.livehover.v2.Boot1xRequestMapping; +import org.springframework.ide.vscode.boot.java.livehover.v2.LiveBeansModel; +import org.springframework.ide.vscode.boot.java.livehover.v2.LiveConditional; +import org.springframework.ide.vscode.boot.java.livehover.v2.LiveConditionalParser; +import org.springframework.ide.vscode.boot.java.livehover.v2.LiveProperties; +import org.springframework.ide.vscode.boot.java.livehover.v2.LivePropertiesJsonParser; +import org.springframework.ide.vscode.boot.java.livehover.v2.RequestMapping; +import org.springframework.ide.vscode.boot.java.livehover.v2.RequestMappingsParser20; import org.springframework.ide.vscode.commons.util.AsyncRunner; import org.springframework.ide.vscode.commons.util.ExceptionUtil; import org.springframework.ide.vscode.commons.util.FuctionWithException; 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 45e9495b9..48e79fe48 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 @@ -15,9 +15,10 @@ import java.util.List; import java.util.Optional; import java.util.Properties; -import org.springframework.ide.vscode.commons.boot.app.cli.livebean.LiveBeansModel; -import org.springframework.ide.vscode.commons.boot.app.cli.liveproperties.LiveProperties; -import org.springframework.ide.vscode.commons.boot.app.cli.requestmappings.RequestMapping; +import org.springframework.ide.vscode.boot.java.livehover.v2.LiveBeansModel; +import org.springframework.ide.vscode.boot.java.livehover.v2.LiveConditional; +import org.springframework.ide.vscode.boot.java.livehover.v2.LiveProperties; +import org.springframework.ide.vscode.boot.java.livehover.v2.RequestMapping; import reactor.core.Disposable; diff --git a/headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/SpringBootAppCLI.java b/headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/SpringBootAppCLI.java index fc74efc26..e7a4841da 100644 --- a/headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/SpringBootAppCLI.java +++ b/headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/SpringBootAppCLI.java @@ -30,9 +30,9 @@ public class SpringBootAppCLI { System.out.println("Spring Boot App: " + app.getProcessID()); System.out.println("Name: " + app.getProcessName()); System.out.println("Port: " + app.getPort()); - System.out.println("Beans: " + app.getBeans()); - System.out.println("Mappings: " + app.getRequestMappings()); - System.out.println("ConfigReport: " + app.getLiveConditionals()); +// System.out.println("Beans: " + app.getBeans()); +// System.out.println("Mappings: " + app.getRequestMappings()); +// System.out.println("ConfigReport: " + app.getLiveConditionals()); System.out.println(); } diff --git a/headless-services/commons/commons-boot-app-cli/src/test/java/org/springframework/ide/vscode/commons/boot/app/cli/Boot1xRequestMappingTest.java b/headless-services/commons/commons-boot-app-cli/src/test/java/org/springframework/ide/vscode/commons/boot/app/cli/Boot1xRequestMappingTest.java index a0644e43f..a27feb434 100644 --- a/headless-services/commons/commons-boot-app-cli/src/test/java/org/springframework/ide/vscode/commons/boot/app/cli/Boot1xRequestMappingTest.java +++ b/headless-services/commons/commons-boot-app-cli/src/test/java/org/springframework/ide/vscode/commons/boot/app/cli/Boot1xRequestMappingTest.java @@ -13,8 +13,8 @@ package org.springframework.ide.vscode.commons.boot.app.cli; import static org.junit.Assert.assertEquals; import org.junit.Test; -import org.springframework.ide.vscode.commons.boot.app.cli.requestmappings.AbstractRequestMapping; -import org.springframework.ide.vscode.commons.boot.app.cli.requestmappings.Boot1xRequestMapping; +import org.springframework.ide.vscode.boot.java.livehover.v2.AbstractRequestMapping; +import org.springframework.ide.vscode.boot.java.livehover.v2.Boot1xRequestMapping; /** * @author Martin Lippert diff --git a/headless-services/commons/commons-boot-app-cli/src/test/java/org/springframework/ide/vscode/commons/boot/app/cli/Boot2xRequestMappingsTest.java b/headless-services/commons/commons-boot-app-cli/src/test/java/org/springframework/ide/vscode/commons/boot/app/cli/Boot2xRequestMappingsTest.java index 143862e39..7725114d2 100644 --- a/headless-services/commons/commons-boot-app-cli/src/test/java/org/springframework/ide/vscode/commons/boot/app/cli/Boot2xRequestMappingsTest.java +++ b/headless-services/commons/commons-boot-app-cli/src/test/java/org/springframework/ide/vscode/commons/boot/app/cli/Boot2xRequestMappingsTest.java @@ -19,8 +19,8 @@ import java.util.stream.Collectors; import org.apache.commons.io.IOUtils; import org.json.JSONObject; import org.junit.Test; -import org.springframework.ide.vscode.commons.boot.app.cli.requestmappings.RequestMapping; -import org.springframework.ide.vscode.commons.boot.app.cli.requestmappings.RequestMappingsParser20; +import org.springframework.ide.vscode.boot.java.livehover.v2.RequestMapping; +import org.springframework.ide.vscode.boot.java.livehover.v2.RequestMappingsParser20; import com.google.common.collect.ImmutableSet; diff --git a/headless-services/commons/commons-boot-app-cli/src/test/java/org/springframework/ide/vscode/commons/boot/app/cli/LiveBeansModelTest.java b/headless-services/commons/commons-boot-app-cli/src/test/java/org/springframework/ide/vscode/commons/boot/app/cli/LiveBeansModelTest.java index 637904786..a6fd471b1 100644 --- a/headless-services/commons/commons-boot-app-cli/src/test/java/org/springframework/ide/vscode/commons/boot/app/cli/LiveBeansModelTest.java +++ b/headless-services/commons/commons-boot-app-cli/src/test/java/org/springframework/ide/vscode/commons/boot/app/cli/LiveBeansModelTest.java @@ -19,8 +19,8 @@ import java.util.List; import org.apache.commons.io.IOUtils; import org.junit.Test; -import org.springframework.ide.vscode.commons.boot.app.cli.livebean.LiveBean; -import org.springframework.ide.vscode.commons.boot.app.cli.livebean.LiveBeansModel; +import org.springframework.ide.vscode.boot.java.livehover.v2.LiveBean; +import org.springframework.ide.vscode.boot.java.livehover.v2.LiveBeansModel; /** * @author Martin Lippert diff --git a/headless-services/commons/commons-boot-app-cli/src/test/java/org/springframework/ide/vscode/commons/boot/app/cli/LocalSpringBootAppTest.java b/headless-services/commons/commons-boot-app-cli/src/test/java/org/springframework/ide/vscode/commons/boot/app/cli/LocalSpringBootAppTest.java index 2b8c66333..83a688134 100644 --- a/headless-services/commons/commons-boot-app-cli/src/test/java/org/springframework/ide/vscode/commons/boot/app/cli/LocalSpringBootAppTest.java +++ b/headless-services/commons/commons-boot-app-cli/src/test/java/org/springframework/ide/vscode/commons/boot/app/cli/LocalSpringBootAppTest.java @@ -30,8 +30,9 @@ import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Ignore; import org.junit.Test; -import org.springframework.ide.vscode.commons.boot.app.cli.livebean.LiveBeansModel; -import org.springframework.ide.vscode.commons.boot.app.cli.requestmappings.RequestMapping; +import org.springframework.ide.vscode.boot.java.livehover.v2.LiveBeansModel; +import org.springframework.ide.vscode.boot.java.livehover.v2.LiveConditional; +import org.springframework.ide.vscode.boot.java.livehover.v2.RequestMapping; import org.springframework.ide.vscode.commons.util.AsyncProcess; import org.springframework.ide.vscode.commons.util.ExceptionUtil; import org.springframework.ide.vscode.commons.util.ExternalCommand; diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/BootJavaLanguageServerComponents.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/BootJavaLanguageServerComponents.java index bb10ab96a..b9e83740a 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/BootJavaLanguageServerComponents.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/BootJavaLanguageServerComponents.java @@ -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 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) { diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/autowired/AutowiredHoverProvider.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/autowired/AutowiredHoverProvider.java index 9da351198..00e65fd17 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/autowired/AutowiredHoverProvider.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/autowired/AutowiredHoverProvider.java @@ -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 getLiveHintCodeLenses(IJavaProject project, Annotation annotation, TextDocument doc, - SpringBootApp[] runningApps) { + SpringProcessLiveData[] processLiveData) { ImmutableList.Builder 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 relevantBeans = getRelevantAutowiredBeans(project, declarationNode, app, + for (SpringProcessLiveData liveData : processLiveData) { + List 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 autowiredBeans = getRelevantAutowiredBeans(project, declarationNode, app, definedBean); + List 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 getRelevantAutowiredBeans(IJavaProject project, ASTNode declarationNode, SpringBootApp app, LiveBean definedBean) { - List relevantBeans = LiveHoverUtils.findRelevantBeans(app, definedBean); - return getRelevantAutowiredBeans(project, declarationNode, app, relevantBeans); + public static List getRelevantAutowiredBeans(IJavaProject project, ASTNode declarationNode, SpringProcessLiveData liveData, LiveBean definedBean) { + List relevantBeans = LiveHoverUtils.findRelevantBeans(liveData, definedBean); + return getRelevantAutowiredBeans(project, declarationNode, liveData, relevantBeans); } - public static List getRelevantAutowiredBeans(IJavaProject project, ASTNode declarationNode, SpringBootApp app, List relevantBeans) { + public static List getRelevantAutowiredBeans(IJavaProject project, ASTNode declarationNode, SpringProcessLiveData liveData, List relevantBeans) { if (!relevantBeans.isEmpty()) { - List allDependencyBeans = LiveHoverUtils.findAllDependencyBeans(app, relevantBeans); + List 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 getLiveHintCodeLenses(IJavaProject project, MethodDeclaration methodDeclaration, - TextDocument doc, SpringBootApp[] runningApps) { + TextDocument doc, SpringProcessLiveData[] processLiveData) { ImmutableList.Builder 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 relevantBeans = getRelevantAutowiredBeans(project, methodDeclaration, app, + for (SpringProcessLiveData liveData : processLiveData) { + List 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)); } } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/conditionals/ConditionalsLiveHoverProvider.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/conditionals/ConditionalsLiveHoverProvider.java index cbf601858..d2c868628 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/conditionals/ConditionalsLiveHoverProvider.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/conditionals/ConditionalsLiveHoverProvider.java @@ -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 getLiveHintCodeLenses(IJavaProject project, Annotation annotation, TextDocument doc, SpringBootApp[] runningApps) { + public Collection getLiveHintCodeLenses(IJavaProject project, Annotation annotation, TextDocument doc, SpringProcessLiveData[] processLiveData) { try { - Optional> val = getMatchedLiveConditionals(annotation, runningApps); + Optional> 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> getMatchedLiveConditionals(Annotation annotation, - SpringBootApp[] runningApps) throws Exception { - if (runningApps != null) { + SpringProcessLiveData[] processLiveData) throws Exception { + if (processLiveData != null) { List 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> hoverContent = new ArrayList<>(); - Optional> val = getMatchedLiveConditionals(annotation, runningApps); + Optional> val = getMatchedLiveConditionals(annotation, processLiveData); if (val.isPresent()) { addHoverContent(val.get(), hoverContent); diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/BootJavaHoverProvider.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/BootJavaHoverProvider.java index 97f91ddfe..8f3760097 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/BootJavaHoverProvider.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/BootJavaHoverProvider.java @@ -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 hoverProviders; - private RunningAppProvider runningAppProvider; - - private Collection runningSpringBootApps; - + private final JavaProjectFinder projectFinder; + private final BootJavaLanguageServerComponents server; + private final AnnotationHierarchyAwareLookup hoverProviders; + private final SpringProcessLiveDataProvider liveDataProvider; public BootJavaHoverProvider(BootJavaLanguageServerComponents server, JavaProjectFinder projectFinder, - AnnotationHierarchyAwareLookup specificProviders, RunningAppProvider runningAppProvider) { + AnnotationHierarchyAwareLookup 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 result) { + SpringProcessLiveData[] processLiveData, Collection result) { Collection providers = this.hoverProviders.getAll(); for (HoverProvider provider : providers) { - Collection hints = provider.getLiveHintCodeLenses(project, methodDeclaration, doc, runningApps); + Collection 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 result) { + SpringProcessLiveData[] processLiveData, Collection result) { Collection providers = this.hoverProviders.getAll(); for (HoverProvider provider : providers) { - Collection hints = provider.getLiveHintCodeLenses(project, typeDeclaration, doc, runningApps); + Collection 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 result) { + SpringProcessLiveData[] processLiveData, Collection result) { ITypeBinding type = annotation.resolveTypeBinding(); if (type != null) { for (HoverProvider provider : this.hoverProviders.get(type)) { - Collection hints = provider.getLiveHintCodeLenses(project, annotation, doc, runningApps); + Collection 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 allApps = this.runningSpringBootApps; - if (allApps == null) { - allApps = runningAppProvider.getAllRunningSpringApps(); - } - - Collection 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 allRunningSpringBootApps) { - this.runningSpringBootApps = allRunningSpringBootApps; - } - } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/HoverProvider.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/HoverProvider.java index 225837732..07452aa61 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/HoverProvider.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/HoverProvider.java @@ -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 getLiveHintCodeLenses(IJavaProject project, Annotation annotation, TextDocument doc, SpringBootApp[] runningApps) { + default Collection getLiveHintCodeLenses(IJavaProject project, Annotation annotation, TextDocument doc, SpringProcessLiveData[] processLiveData) { return null; } - default Collection getLiveHintCodeLenses(IJavaProject project,TypeDeclaration typeDeclaration, TextDocument doc, SpringBootApp[] runningApps) { + default Collection getLiveHintCodeLenses(IJavaProject project,TypeDeclaration typeDeclaration, TextDocument doc, SpringProcessLiveData[] processLiveData) { return null; } - default Collection getLiveHintCodeLenses(IJavaProject project, MethodDeclaration methodDeclaration, TextDocument doc, SpringBootApp[] runningApps) { + default Collection getLiveHintCodeLenses(IJavaProject project, MethodDeclaration methodDeclaration, TextDocument doc, SpringProcessLiveData[] processLiveData) { return null; } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/RemoteRunningAppsProvider.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/RemoteRunningAppsProvider.java index f11c91a2c..514c68eac 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/RemoteRunningAppsProvider.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/RemoteRunningAppsProvider.java @@ -143,7 +143,7 @@ public class RemoteRunningAppsProvider implements RunningAppProvider { private Map 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 newAppData = new HashSet<>(Arrays.asList(appData)); - { //Remove obsolete apps - Iterator> entries = remoteAppInstances.entrySet().iterator(); - while (entries.hasNext()) { - Entry 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 newAppData = new HashSet<>(Arrays.asList(appData)); +// { //Remove obsolete apps +// Iterator> entries = remoteAppInstances.entrySet().iterator(); +// while (entries.hasNext()) { +// Entry 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()); +// }); +// } +// } } } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/AbstractInjectedIntoHoverProvider.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/AbstractInjectedIntoHoverProvider.java index 2e89fdfab..e1218771f 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/AbstractInjectedIntoHoverProvider.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/AbstractInjectedIntoHoverProvider.java @@ -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 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 nameRange = ASTUtils.nameRange(doc, annotation); if (nameRange.isPresent()) { - List codeLenses = assembleCodeLenses(project, runningApps, app -> definedBean, doc, nameRange.get(), annotation); + List 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 nameRange = ASTUtils.nameRange(doc, annotation); if (nameRange.isPresent()) { @@ -102,22 +102,22 @@ public abstract class AbstractInjectedIntoHoverProvider implements HoverProvider return null; } - protected List assembleCodeLenses(IJavaProject project, SpringBootApp[] runningApps, DefinedBeanProvider definedBeanProvider, + protected List 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 relevantBeans = LiveHoverUtils.findRelevantBeans(app, definedBean); + List relevantBeans = LiveHoverUtils.findRelevantBeans(liveData, definedBean); if (!relevantBeans.isEmpty()) { - List injectedBeans = getRelevantInjectedIntoBeans(project, app, definedBean, relevantBeans); + List injectedBeans = getRelevantInjectedIntoBeans(project, liveData, definedBean, relevantBeans); ImmutableList.Builder 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 wiredBeans = findWiredBeans(project, app, relevantBeans, node); - builder.addAll(assembleCodeLenseForAutowired(wiredBeans, project, app, doc, range, node)); + List wiredBeans = findWiredBeans(project, liveData, relevantBeans, node); + builder.addAll(assembleCodeLenseForAutowired(wiredBeans, project, liveData, doc, range, node)); List 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 assembleCodeLenseForAutowired(List wiredBeans, IJavaProject project, SpringBootApp app, TextDocument doc, Range nameRange, ASTNode astNode) { + protected List assembleCodeLenseForAutowired(List 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 findWiredBeans(IJavaProject project, SpringBootApp app, List relevantBeans, ASTNode astNode) { + protected List findWiredBeans(IJavaProject project, SpringProcessLiveData liveData, List 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 relevantBeans = LiveHoverUtils.findRelevantBeans(app, definedBean); + List 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 injectedBeans = getRelevantInjectedIntoBeans(project, app, definedBean, relevantBeans); + List 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 wiredBeans = findWiredBeans(project, app, relevantBeans, astNode); + List 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 getRelevantInjectedIntoBeans(IJavaProject project, SpringBootApp app, LiveBean definedBean, List relevantBeans) { - LiveBeansModel beans = app.getBeans(); + protected List getRelevantInjectedIntoBeans(IJavaProject project, SpringProcessLiveData processLiveData, LiveBean definedBean, List relevantBeans) { + LiveBeansModel beans = processLiveData.getBeans(); if (relevantBeans != null) { return relevantBeans.stream() .flatMap(b -> beans.getBeansDependingOn(b.getId()).stream()) diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/ActiveProfilesProvider.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/ActiveProfilesProvider.java index d9044ff07..1726242eb 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/ActiveProfilesProvider.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/ActiveProfilesProvider.java @@ -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 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 getLiveHintCodeLenses(IJavaProject project, Annotation annotation, TextDocument doc, SpringBootApp[] runningApps) { - if (runningApps.length > 0) { + public Collection getLiveHintCodeLenses(IJavaProject project, Annotation annotation, TextDocument doc, SpringProcessLiveData[] processLiveData) { + if (processLiveData.length > 0) { Builder codeLenses = ImmutableList.builder(); - Set allActiveProfiles = getAllActiveProfiles(runningApps); + Set 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 getAllActiveProfiles(SpringBootApp[] runningApps) { + private static Set getAllActiveProfiles(SpringProcessLiveData[] processLiveData) { ImmutableSet.Builder builder = ImmutableSet.builder(); - for (SpringBootApp app : runningApps) { - List 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(); diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/BeanInjectedIntoHoverProvider.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/BeanInjectedIntoHoverProvider.java index 77e192ac4..09345a77a 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/BeanInjectedIntoHoverProvider.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/BeanInjectedIntoHoverProvider.java @@ -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 findWiredBeans(IJavaProject project, SpringBootApp app, List relevantBeans, ASTNode astNode) { + protected List findWiredBeans(IJavaProject project, SpringProcessLiveData liveData, List 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 assembleCodeLenseForAutowired(List wiredBeans, IJavaProject project, - SpringBootApp app, TextDocument doc, Range nameRange, ASTNode astNode) { + SpringProcessLiveData liveData, TextDocument doc, Range nameRange, ASTNode astNode) { ImmutableList.Builder 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); } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/ComponentInjectionsHoverProvider.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/ComponentInjectionsHoverProvider.java index 127b49a3d..e179d7961 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/ComponentInjectionsHoverProvider.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/ComponentInjectionsHoverProvider.java @@ -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 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 nameRange = Optional.of(ASTUtils.nodeRegion(doc, typeDeclaration.getName()).asRange()); if (nameRange.isPresent()) { - List codeLenses = assembleCodeLenses(project, runningApps, app -> definedBean(app, getBeanType(beanType), id), doc, + List 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 findWiredBeans(IJavaProject project, SpringBootApp app, List relevantBeans, + protected List findWiredBeans(IJavaProject project, SpringProcessLiveData liveData, List 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) { diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/LiveHoverUtils.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/LiveHoverUtils.java index 340510302..a457710e1 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/LiveHoverUtils.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/LiveHoverUtils.java @@ -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 findRelevantBeans(SpringBootApp app, LiveBean definedBean) { - LiveBeansModel beansModel = app.getBeans(); + public static List findRelevantBeans(SpringProcessLiveData liveData, LiveBean definedBean) { + LiveBeansModel beansModel = liveData.getBeans(); if (beansModel != null) { List relevantBeans = beansModel.getBeansOfName(definedBean.getId()); String type = definedBean.getType(true); @@ -159,20 +159,20 @@ public class LiveHoverUtils { return Collections.emptyList(); } - public static List findAllDependencyBeans(SpringBootApp app, List relevantBeans) { - LiveBeansModel beans = app.getBeans(); + public static List findAllDependencyBeans(SpringProcessLiveData liveData, List 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 createCodeLensForMethodParameters(SpringBootApp app, IJavaProject project, MethodDeclaration method, TextDocument doc, List wiredBeans) { + public static List createCodeLensForMethodParameters(SpringProcessLiveData liveData, IJavaProject project, MethodDeclaration method, TextDocument doc, List wiredBeans) { ImmutableList.Builder builder = ImmutableList.builder(); method.parameters().forEach(p -> { if (p instanceof SingleVariableDeclaration) { diff --git a/headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/livebean/LiveBean.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/LiveBean.java similarity index 97% rename from headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/livebean/LiveBean.java rename to headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/LiveBean.java index b710eb156..c2f46f266 100644 --- a/headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/livebean/LiveBean.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/LiveBean.java @@ -8,7 +8,7 @@ * Contributors: * Pivotal, Inc. - initial API and implementation *******************************************************************************/ -package org.springframework.ide.vscode.commons.boot.app.cli.livebean; +package org.springframework.ide.vscode.boot.java.livehover.v2; import java.util.Arrays; diff --git a/headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/livebean/LiveBeansModel.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/LiveBeansModel.java similarity index 96% rename from headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/livebean/LiveBeansModel.java rename to headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/LiveBeansModel.java index 3379d445a..ead2fb62c 100644 --- a/headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/livebean/LiveBeansModel.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/LiveBeansModel.java @@ -8,7 +8,7 @@ * Contributors: * Pivotal, Inc. - initial API and implementation *******************************************************************************/ -package org.springframework.ide.vscode.commons.boot.app.cli.livebean; +package org.springframework.ide.vscode.boot.java.livehover.v2; import java.util.ArrayList; import java.util.List; @@ -16,7 +16,8 @@ import java.util.Set; import org.json.JSONArray; import org.json.JSONObject; -import org.springframework.ide.vscode.commons.util.Log; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.ide.vscode.commons.util.StringUtil; import com.google.common.collect.ImmutableListMultimap; @@ -27,6 +28,8 @@ import com.google.common.collect.ImmutableListMultimap; */ public class LiveBeansModel { + private static final Logger log = LoggerFactory.getLogger(LiveBeansModel.class); + private static final String[] NO_STRINGS = new String[] {}; public static class Builder { @@ -196,7 +199,7 @@ public class LiveBeansModel { //Only getting here if none of the parsers worked... So if at least one parser works, // we won't log any exceptions. for (Exception e : exceptions) { - Log.log(e); + log.warn(e.getMessage()); } return LiveBeansModel.builder().build(); // always return at least an empty model. } diff --git a/headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/LiveConditional.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/LiveConditional.java similarity index 97% rename from headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/LiveConditional.java rename to headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/LiveConditional.java index 5599ef2e3..6556dae13 100644 --- a/headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/LiveConditional.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/LiveConditional.java @@ -8,7 +8,7 @@ * Contributors: * Pivotal, Inc. - initial API and implementation *******************************************************************************/ -package org.springframework.ide.vscode.commons.boot.app.cli; +package org.springframework.ide.vscode.boot.java.livehover.v2; public class LiveConditional { diff --git a/headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/LiveConditionalParser.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/LiveConditionalParser.java similarity index 92% rename from headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/LiveConditionalParser.java rename to headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/LiveConditionalParser.java index a65871752..7063a59c6 100644 --- a/headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/LiveConditionalParser.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/LiveConditionalParser.java @@ -8,7 +8,7 @@ * Contributors: * Pivotal, Inc. - initial API and implementation *******************************************************************************/ -package org.springframework.ide.vscode.commons.boot.app.cli; +package org.springframework.ide.vscode.boot.java.livehover.v2; import java.util.ArrayList; import java.util.List; @@ -16,7 +16,8 @@ import java.util.Optional; import org.json.JSONArray; import org.json.JSONObject; -import org.springframework.ide.vscode.commons.util.Log; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.ide.vscode.commons.util.StringUtil; /** @@ -30,6 +31,8 @@ import org.springframework.ide.vscode.commons.util.StringUtil; * */ public class LiveConditionalParser { + + private static final Logger log = LoggerFactory.getLogger(LiveConditionalParser.class); private final String autoConfigRecord; private final String appProcessName; @@ -41,7 +44,7 @@ public class LiveConditionalParser { this.appProcessName = appProcessName; } - public Optional> parse() { + public LiveConditional[] parse() { try { List allConditionals = new ArrayList<>(); if (StringUtil.hasText(autoConfigRecord)) { @@ -58,12 +61,12 @@ public class LiveConditionalParser { } } if (!allConditionals.isEmpty()) { - return Optional.of(allConditionals); + return (LiveConditional[]) allConditionals.toArray(new LiveConditional[allConditionals.size()]); } } catch (Exception e) { - Log.log(e); + log.warn(e.getMessage()); } - return Optional.empty(); + return new LiveConditional[0]; } /** @@ -152,7 +155,7 @@ public class LiveConditionalParser { } - public static Optional> parse(String autoConfigRecord, String appProcessId, + public static LiveConditional[] parse(String autoConfigRecord, String appProcessId, String appProcessName) { return new LiveConditionalParser(autoConfigRecord, appProcessId, appProcessName).parse(); } diff --git a/headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/liveproperties/LiveProperties.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/LiveProperties.java similarity index 93% rename from headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/liveproperties/LiveProperties.java rename to headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/LiveProperties.java index 03c7f7dde..26fd3bbb3 100644 --- a/headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/liveproperties/LiveProperties.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/LiveProperties.java @@ -8,7 +8,7 @@ * Contributors: * Pivotal, Inc. - initial API and implementation *******************************************************************************/ -package org.springframework.ide.vscode.commons.boot.app.cli.liveproperties; +package org.springframework.ide.vscode.boot.java.livehover.v2; import java.util.ArrayList; import java.util.List; @@ -23,7 +23,6 @@ public class LiveProperties { this.sources = sources != null ? ImmutableList.copyOf(sources) : ImmutableList.of(); } - public List getProperties(String propertyName) { List foundProperties = new ArrayList<>(); for (LivePropertySource source : sources) { diff --git a/headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/liveproperties/LivePropertiesJsonParser.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/LivePropertiesJsonParser.java similarity index 95% rename from headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/liveproperties/LivePropertiesJsonParser.java rename to headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/LivePropertiesJsonParser.java index 8a901bf4f..991756813 100644 --- a/headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/liveproperties/LivePropertiesJsonParser.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/LivePropertiesJsonParser.java @@ -8,7 +8,7 @@ * Contributors: * Pivotal, Inc. - initial API and implementation *******************************************************************************/ -package org.springframework.ide.vscode.commons.boot.app.cli.liveproperties; +package org.springframework.ide.vscode.boot.java.livehover.v2; import java.util.List; diff --git a/headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/liveproperties/LivePropertiesJsonParser1x.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/LivePropertiesJsonParser1x.java similarity index 97% rename from headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/liveproperties/LivePropertiesJsonParser1x.java rename to headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/LivePropertiesJsonParser1x.java index 7e80bf152..00cf2821b 100644 --- a/headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/liveproperties/LivePropertiesJsonParser1x.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/LivePropertiesJsonParser1x.java @@ -8,7 +8,7 @@ * Contributors: * Pivotal, Inc. - initial API and implementation *******************************************************************************/ -package org.springframework.ide.vscode.commons.boot.app.cli.liveproperties; +package org.springframework.ide.vscode.boot.java.livehover.v2; import java.util.Iterator; import java.util.List; diff --git a/headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/liveproperties/LivePropertiesJsonParser2x.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/LivePropertiesJsonParser2x.java similarity index 97% rename from headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/liveproperties/LivePropertiesJsonParser2x.java rename to headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/LivePropertiesJsonParser2x.java index 7d2cc54b3..d42669e6b 100644 --- a/headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/liveproperties/LivePropertiesJsonParser2x.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/LivePropertiesJsonParser2x.java @@ -8,7 +8,7 @@ * Contributors: * Pivotal, Inc. - initial API and implementation *******************************************************************************/ -package org.springframework.ide.vscode.commons.boot.app.cli.liveproperties; +package org.springframework.ide.vscode.boot.java.livehover.v2; import java.util.ArrayList; import java.util.Iterator; diff --git a/headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/liveproperties/LiveProperty.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/LiveProperty.java similarity index 94% rename from headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/liveproperties/LiveProperty.java rename to headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/LiveProperty.java index 4fa246249..12791297a 100644 --- a/headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/liveproperties/LiveProperty.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/LiveProperty.java @@ -8,7 +8,7 @@ * Contributors: * Pivotal, Inc. - initial API and implementation *******************************************************************************/ -package org.springframework.ide.vscode.commons.boot.app.cli.liveproperties; +package org.springframework.ide.vscode.boot.java.livehover.v2; public class LiveProperty { diff --git a/headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/liveproperties/LivePropertySource.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/LivePropertySource.java similarity index 93% rename from headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/liveproperties/LivePropertySource.java rename to headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/LivePropertySource.java index 87d0646fd..f91a481eb 100644 --- a/headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/liveproperties/LivePropertySource.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/LivePropertySource.java @@ -8,7 +8,7 @@ * Contributors: * Pivotal, Inc. - initial API and implementation *******************************************************************************/ -package org.springframework.ide.vscode.commons.boot.app.cli.liveproperties; +package org.springframework.ide.vscode.boot.java.livehover.v2; import java.util.List; @@ -19,7 +19,6 @@ public class LivePropertySource { private final List properties; private final String sourceName; - public LivePropertySource(String sourceName, List properties) { this.sourceName = sourceName; this.properties = properties != null ? ImmutableList.copyOf(properties) : ImmutableList.of(); diff --git a/headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/requestmappings/RequestMapping.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/LiveRequestMapping.java similarity index 87% rename from headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/requestmappings/RequestMapping.java rename to headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/LiveRequestMapping.java index 31be271a7..ee086095f 100644 --- a/headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/requestmappings/RequestMapping.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/LiveRequestMapping.java @@ -8,11 +8,11 @@ * Contributors: * Pivotal, Inc. - initial API and implementation *******************************************************************************/ -package org.springframework.ide.vscode.commons.boot.app.cli.requestmappings; +package org.springframework.ide.vscode.boot.java.livehover.v2; import java.util.Set; -public interface RequestMapping { +public interface LiveRequestMapping { // String getPath(); commented... because... not used?? String[] getSplitPath(); String getFullyQualifiedClassName(); diff --git a/headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/requestmappings/AbstractRequestMapping.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/LiveRequestMappingAbstractImpl.java similarity index 85% rename from headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/requestmappings/AbstractRequestMapping.java rename to headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/LiveRequestMappingAbstractImpl.java index e7e588ca5..0fea71b92 100644 --- a/headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/requestmappings/AbstractRequestMapping.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/LiveRequestMappingAbstractImpl.java @@ -8,7 +8,7 @@ * Contributors: * Pivotal, Inc. - initial API and implementation *******************************************************************************/ -package org.springframework.ide.vscode.commons.boot.app.cli.requestmappings; +package org.springframework.ide.vscode.boot.java.livehover.v2; import java.util.Arrays; import java.util.Collections; @@ -23,7 +23,7 @@ import org.springframework.ide.vscode.commons.java.parser.JLRMethodParser.JLRMet import com.google.common.base.Supplier; import com.google.common.base.Suppliers; -public abstract class AbstractRequestMapping implements RequestMapping { +public abstract class LiveRequestMappingAbstractImpl implements LiveRequestMapping { private static final Pattern REQUEST_METHODS_PATTERN = Pattern.compile(".*methods=\\[(.*)\\].*"); @@ -31,7 +31,7 @@ public abstract class AbstractRequestMapping implements RequestMapping { final private Supplier> requestMethodsSupplier; final private Supplier pathsSuplier; - public AbstractRequestMapping() { + public LiveRequestMappingAbstractImpl() { this.requestMethodsSupplier = Suppliers.memoize(() -> parseRequestMethods()); this.methodDataSupplier = Suppliers.memoize(() -> JLRMethodParser.parse(getMethodString())); this.pathsSuplier = Suppliers.memoize(() -> computePaths()); @@ -90,13 +90,13 @@ public abstract class AbstractRequestMapping implements RequestMapping { int end = predicate.indexOf(']'); if (end>=2) { String pathString = predicate.substring(start, end); - return ParseUtil.splitPaths(pathString); + return splitPaths(pathString); } } //Case 1, or some unanticipated stuff. //Assume the key is the paths strk g, which is right for Case 1 // and probably more useful than null for 'unanticipated stuff'. - return ParseUtil.splitPaths(predicate); + return splitPaths(predicate); } protected abstract String getPredicateString(); @@ -106,4 +106,18 @@ public abstract class AbstractRequestMapping implements RequestMapping { return pathsSuplier.get(); } + protected String[] splitPaths(String paths) { + return Arrays.stream(paths.split("\\|\\|")) + .map(s -> s.trim()) + .filter(s -> !s.isEmpty()) + .map(s -> { + if (s.charAt(0) != '/') { + return '/' + s; + } else { + return s; + } + }) + .toArray(String[]::new); + } + } diff --git a/headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/requestmappings/Boot1xRequestMapping.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/LiveRequestMappingBoot1xRequestMapping.java similarity index 82% rename from headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/requestmappings/Boot1xRequestMapping.java rename to headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/LiveRequestMappingBoot1xRequestMapping.java index f0b530666..02ba8ecfe 100644 --- a/headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/requestmappings/Boot1xRequestMapping.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/LiveRequestMappingBoot1xRequestMapping.java @@ -8,13 +8,13 @@ * Contributors: * Pivotal, Inc. - initial API and implementation *******************************************************************************/ -package org.springframework.ide.vscode.commons.boot.app.cli.requestmappings; +package org.springframework.ide.vscode.boot.java.livehover.v2; import org.json.JSONObject; import com.google.common.base.Objects; -public class Boot1xRequestMapping extends AbstractRequestMapping { +public class LiveRequestMappingBoot1xRequestMapping extends LiveRequestMappingAbstractImpl { /* There are two styles of entries: @@ -34,7 +34,7 @@ There are two styles of entries: private JSONObject beanInfo; private String pathKey; - public Boot1xRequestMapping(String pathKey, JSONObject beanInfo) { + public LiveRequestMappingBoot1xRequestMapping(String pathKey, JSONObject beanInfo) { this.pathKey = pathKey; this.beanInfo = beanInfo; } @@ -52,7 +52,7 @@ There are two styles of entries: return false; if (getClass() != obj.getClass()) return false; - Boot1xRequestMapping other = (Boot1xRequestMapping) obj; + LiveRequestMappingBoot1xRequestMapping other = (LiveRequestMappingBoot1xRequestMapping) obj; return Objects.equal(this.pathKey, other.pathKey) && Objects.equal(this.getMethodString(), other.getMethodString()); } diff --git a/headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/requestmappings/Boot20DispatcherServletMapping.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/LiveRequestMappingBoot2xDispatcherServletMapping.java similarity index 95% rename from headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/requestmappings/Boot20DispatcherServletMapping.java rename to headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/LiveRequestMappingBoot2xDispatcherServletMapping.java index ba610ee53..51f49b1e9 100644 --- a/headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/requestmappings/Boot20DispatcherServletMapping.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/LiveRequestMappingBoot2xDispatcherServletMapping.java @@ -8,7 +8,7 @@ * Contributors: * Pivotal, Inc. - initial API and implementation *******************************************************************************/ -package org.springframework.ide.vscode.commons.boot.app.cli.requestmappings; +package org.springframework.ide.vscode.boot.java.livehover.v2; import java.util.ArrayList; import java.util.Collections; @@ -20,7 +20,7 @@ import org.json.JSONArray; import org.json.JSONObject; import org.objectweb.asm.Type; -public class Boot20DispatcherServletMapping implements RequestMapping { +public class LiveRequestMappingBoot2xDispatcherServletMapping implements LiveRequestMapping { /* @@ -54,7 +54,7 @@ Example entry: private JSONObject data; - public Boot20DispatcherServletMapping(JSONObject data) { + public LiveRequestMappingBoot2xDispatcherServletMapping(JSONObject data) { this.data = data; } diff --git a/headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/requestmappings/RequestMappingsParser20.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/LiveRequestMappingBoot2xParser.java similarity index 80% rename from headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/requestmappings/RequestMappingsParser20.java rename to headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/LiveRequestMappingBoot2xParser.java index ddc25e8fc..75d9e8890 100644 --- a/headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/requestmappings/RequestMappingsParser20.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/LiveRequestMappingBoot2xParser.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2018 Pivotal, Inc. + * Copyright (c) 2018, 2019 Pivotal, Inc. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -8,20 +8,19 @@ * Contributors: * Pivotal, Inc. - initial API and implementation *******************************************************************************/ -package org.springframework.ide.vscode.commons.boot.app.cli.requestmappings; +package org.springframework.ide.vscode.boot.java.livehover.v2; import java.util.ArrayList; -import java.util.Collection; import java.util.List; import org.json.JSONArray; import org.json.JSONObject; -public class RequestMappingsParser20 { +public class LiveRequestMappingBoot2xParser { - public static Collection parse(JSONObject obj) { + public static LiveRequestMapping[] parse(JSONObject obj) { obj = obj.getJSONObject("contexts"); - List result = new ArrayList<>(); + List result = new ArrayList<>(); for (String contextId : obj.keySet()) { //Contains 3 different keys now ('dispatcherServlets', 'servletFilters' and 'servlets'. // Each with their own kind of data inside. Looks like 'dispatcherServlets' contains stuff similar to what we @@ -40,11 +39,11 @@ public class RequestMappingsParser20 { if (rmArray != null) { for (Object e : rmArray) { JSONObject rm = (JSONObject)e; - result.add(new Boot20DispatcherServletMapping(rm)); + result.add(new LiveRequestMappingBoot2xDispatcherServletMapping(rm)); } } } - return result; + return (LiveRequestMapping[]) result.toArray(new LiveRequestMapping[result.size()]); } } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/SpringProcessCommandHandler.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/SpringProcessCommandHandler.java new file mode 100644 index 000000000..cd3578f42 --- /dev/null +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/SpringProcessCommandHandler.java @@ -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 { + +} diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/SpringProcessConnector.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/SpringProcessConnector.java new file mode 100644 index 000000000..8ac948151 --- /dev/null +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/SpringProcessConnector.java @@ -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; + +} diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/SpringProcessConnectorLocal.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/SpringProcessConnectorLocal.java new file mode 100644 index 000000000..aabb72311 --- /dev/null +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/SpringProcessConnectorLocal.java @@ -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 projects; + private final Set 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 currentVms = VirtualMachine.list(); + Set 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 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); + } + +} diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/SpringProcessConnectorOverJMX.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/SpringProcessConnectorOverJMX.java new file mode 100644 index 000000000..7952f0592 --- /dev/null +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/SpringProcessConnectorOverJMX.java @@ -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); + } + +} diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/SpringProcessConnectorRemote.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/SpringProcessConnectorRemote.java new file mode 100644 index 000000000..bbde6949e --- /dev/null +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/SpringProcessConnectorRemote.java @@ -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 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 newAppData = new HashSet<>(Arrays.asList(appData)); + + Iterator> entries = remoteAppInstances.entrySet().iterator(); + while (entries.hasNext()) { + Entry 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"; + } + +} diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/SpringProcessConnectorService.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/SpringProcessConnectorService.java new file mode 100644 index 000000000..c05314f92 --- /dev/null +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/SpringProcessConnectorService.java @@ -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 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); + } + +} diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/SpringProcessLiveData.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/SpringProcessLiveData.java new file mode 100644 index 000000000..7cd164a2e --- /dev/null +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/SpringProcessLiveData.java @@ -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; + } + +} diff --git a/headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/requestmappings/ParseUtil.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/SpringProcessLiveDataChangeEvent.java similarity index 51% rename from headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/requestmappings/ParseUtil.java rename to headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/SpringProcessLiveDataChangeEvent.java index c23729abd..0e81ea2ac 100644 --- a/headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/requestmappings/ParseUtil.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/SpringProcessLiveDataChangeEvent.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2018 Pivotal, Inc. + * Copyright (c) 2019 Pivotal, Inc. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -8,24 +8,21 @@ * Contributors: * Pivotal, Inc. - initial API and implementation *******************************************************************************/ -package org.springframework.ide.vscode.commons.boot.app.cli.requestmappings; +package org.springframework.ide.vscode.boot.java.livehover.v2; -import java.util.Arrays; +/** + * @author Martin Lippert + */ +public class SpringProcessLiveDataChangeEvent { + + private final SpringProcessLiveData[] updatedLiveData; -public class ParseUtil { - - public static String[] splitPaths(String paths) { - return Arrays.stream(paths.split("\\|\\|")) - .map(s -> s.trim()) - .filter(s -> !s.isEmpty()) - .map(s -> { - if (s.charAt(0) != '/') { - return '/' + s; - } else { - return s; - } - }) - .toArray(String[]::new); + public SpringProcessLiveDataChangeEvent(SpringProcessLiveData[] updatedLiveData) { + this.updatedLiveData = updatedLiveData; + } + + public SpringProcessLiveData[] getUpdatedLiveData() { + return updatedLiveData; } } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/SpringProcessLiveDataChangeListener.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/SpringProcessLiveDataChangeListener.java new file mode 100644 index 000000000..604c07b72 --- /dev/null +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/SpringProcessLiveDataChangeListener.java @@ -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); + +} diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/SpringProcessLiveDataExtractorOverJMX.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/SpringProcessLiveDataExtractorOverJMX.java new file mode 100644 index 000000000..7ded93e9e --- /dev/null +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/SpringProcessLiveDataExtractorOverJMX.java @@ -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 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 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 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 result = new ArrayList<>(); + Iterator 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 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 + * + * 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 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 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; + } + + + +} diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/SpringProcessLiveDataProvider.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/SpringProcessLiveDataProvider.java new file mode 100644 index 000000000..8c415bdf5 --- /dev/null +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/SpringProcessLiveDataProvider.java @@ -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 liveData; + private final List listeners; + + public SpringProcessLiveDataProvider() { + this.liveData = new ConcurrentHashMap<>(); + this.listeners = new CopyOnWriteArrayList<>(); + } + + public SpringProcessLiveData[] getLatestLiveData() { + Collection 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); + } + } + +} diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/SpringProcessLiveHoverUpdater.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/SpringProcessLiveHoverUpdater.java new file mode 100644 index 000000000..455a3c197 --- /dev/null +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/SpringProcessLiveHoverUpdater.java @@ -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> 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()); + 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 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]); + } + +} diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/SpringProcessTracker.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/SpringProcessTracker.java new file mode 100644 index 000000000..cd43f8c17 --- /dev/null +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/SpringProcessTracker.java @@ -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(); + } + } + +} diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/LiveAppURLSymbolProvider.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/LiveAppURLSymbolProvider.java index b59b67dc6..3eb39aadd 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/LiveAppURLSymbolProvider.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/LiveAppURLSymbolProvider.java @@ -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 getSymbols(String query) { List 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; } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/RequestMappingHoverProvider.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/RequestMappingHoverProvider.java index cc8524e0b..82d2f1ae0 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/RequestMappingHoverProvider.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/RequestMappingHoverProvider.java @@ -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 getLiveHintCodeLenses(IJavaProject project, Annotation annotation, TextDocument doc, SpringBootApp[] runningApps) { + public Collection getLiveHintCodeLenses(IJavaProject project, Annotation annotation, TextDocument doc, SpringProcessLiveData[] processLiveData) { try { - if (runningApps.length > 0) { - List> val = getRequestMappingMethodFromRunningApp(annotation, runningApps); + if (processLiveData.length > 0) { + List> val = getRequestMappingMethodFromRunningApp(annotation, processLiveData); if (!val.isEmpty()) { Range hoverRange = doc.toRange(annotation.getStartPosition(), annotation.getLength()); List 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> val = getRequestMappingMethodFromRunningApp(annotation, runningApps); + List> val = getRequestMappingMethodFromRunningApp(annotation, processLiveData); if (!val.isEmpty()) { Hover hover = createHoverWithContent(val); @@ -171,17 +171,17 @@ public class RequestMappingHoverProvider implements HoverProvider { return null; } - private List> getRequestMappingMethodFromRunningApp(Annotation annotation, - SpringBootApp[] runningApps) { + private List> getRequestMappingMethodFromRunningApp(Annotation annotation, + SpringProcessLiveData[] processLiveData) { - List> results = new ArrayList<>(); + List> results = new ArrayList<>(); try { - for (SpringBootApp app : runningApps) { - Collection 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 getUrls(List> mappingMethods) throws Exception { + private List getUrls(List> mappingMethods) throws Exception { List urls = new ArrayList<>(); for (int i = 0; i < mappingMethods.size(); i++) { - Tuple2 mappingMethod = mappingMethods.get(i); - SpringBootApp app = mappingMethod.getT2(); - String contextPath = app.getContextPath(); + Tuple2 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> mappingMethods) throws Exception { + private Hover createHoverWithContent(List> mappingMethods) throws Exception { StringBuilder contentVal = new StringBuilder(); for (int i = 0; i < mappingMethods.size(); i++) { - Tuple2 mappingMethod = mappingMethods.get(i); + Tuple2 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 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) { diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/Change.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/Change.java index 1a8c9cee0..19d9dc22d 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/Change.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/Change.java @@ -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 diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/ChangeHistory.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/ChangeHistory.java index c1eb871f8..8a5197201 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/ChangeHistory.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/ChangeHistory.java @@ -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; } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/NameEnvironmentAwareASTRequestor.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/NameEnvironmentAwareASTRequestor.java new file mode 100644 index 000000000..f6fef51df --- /dev/null +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/NameEnvironmentAwareASTRequestor.java @@ -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()); + } + + } +} diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/RestrictedDefaultSymbolProvider.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/RestrictedDefaultSymbolProvider.java index dcf9e0eff..77ff8bf87 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/RestrictedDefaultSymbolProvider.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/RestrictedDefaultSymbolProvider.java @@ -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 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()); } } } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringLiveChangeDetectionWatchdog.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringLiveChangeDetectionWatchdog.java index 2d61aa7ec..e11581ac2 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringLiveChangeDetectionWatchdog.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringLiveChangeDetectionWatchdog.java @@ -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; diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringLiveHoverWatchdog.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringLiveHoverWatchdog.java index 75ae29d34..54c06a8dc 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringLiveHoverWatchdog.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringLiveHoverWatchdog.java @@ -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> 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); } } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/value/ValueHoverProvider.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/value/ValueHoverProvider.java index f5620bdc7..0dfd54d38 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/value/ValueHoverProvider.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/value/ValueHoverProvider.java @@ -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 allProperties = getPropertiesFromProcesses(runningApps); + Map allProperties = getPropertiesFromProcesses(processLiveData); StringBuilder hover = new StringBuilder(); - for (SpringBootApp app : allProperties.keySet()) { + for (SpringProcessLiveData app : allProperties.keySet()) { LiveProperties properties = allProperties.get(app); List 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 provideHighlightHints(TextDocument doc, StringLiteral node, SpringBootApp[] runningApps) { + private List 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 propRanges = entry.getValue(); - List matchingLiveProperties = findMatchingLiveProperties(runningApps, parsedProp); + List 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 findMatchingLiveProperties(SpringBootApp[] runningApps, String propFromValue) { - Map allProperties = getPropertiesFromProcesses(runningApps); + private List findMatchingLiveProperties(SpringProcessLiveData[] processLiveData, String propFromValue) { + Map allProperties = getPropertiesFromProcesses(processLiveData); - for (SpringBootApp app : allProperties.keySet()) { - LiveProperties properties = allProperties.get(app); + for (SpringProcessLiveData liveData : allProperties.keySet()) { + LiveProperties properties = allProperties.get(liveData); List matchingLiveProperties = properties.getProperties(propFromValue); if (matchingLiveProperties != null && !matchingLiveProperties.isEmpty()) { return matchingLiveProperties; @@ -219,14 +219,14 @@ public class ValueHoverProvider implements HoverProvider { return null; } - public Map getPropertiesFromProcesses(SpringBootApp[] runningApps) { - Map result = new HashMap<>(); + public Map getPropertiesFromProcesses(SpringProcessLiveData[] processLiveData) { + Map 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 getLiveHintCodeLenses(IJavaProject project, Annotation annotation, TextDocument doc, SpringBootApp[] runningApps) { + public Collection getLiveHintCodeLenses(IJavaProject project, Annotation annotation, TextDocument doc, SpringProcessLiveData[] processLiveData) { // Show highlight hints for properties in @Value that have live information List lenses = new ArrayList<>(); annotation.accept(new ASTVisitor() { @Override public boolean visit(StringLiteral node) { - List provideHighlightHints = provideHighlightHints(doc, node, runningApps); + List provideHighlightHints = provideHighlightHints(doc, node, processLiveData); lenses.addAll(provideHighlightHints); return super.visit(node); } diff --git a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/autowired/test/AutowiredHoverProviderTest.java b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/autowired/test/AutowiredHoverProviderTest.java index 798d5ac70..52dd02b27 100644 --- a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/autowired/test/AutowiredHoverProviderTest.java +++ b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/autowired/test/AutowiredHoverProviderTest.java @@ -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; diff --git a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/livehover/test/BeanInjectedIntoHoverProviderTest.java b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/livehover/test/BeanInjectedIntoHoverProviderTest.java index 4ee629ef6..0628b6110 100644 --- a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/livehover/test/BeanInjectedIntoHoverProviderTest.java +++ b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/livehover/test/BeanInjectedIntoHoverProviderTest.java @@ -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; diff --git a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/livehover/test/BeansByTypeHoverProviderTest.java b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/livehover/test/BeansByTypeHoverProviderTest.java index aa076830b..35dca7f92 100644 --- a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/livehover/test/BeansByTypeHoverProviderTest.java +++ b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/livehover/test/BeansByTypeHoverProviderTest.java @@ -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; diff --git a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/livehover/test/ComponentInjectionsHoverProviderTest.java b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/livehover/test/ComponentInjectionsHoverProviderTest.java index 2d4c28b11..299e50666 100644 --- a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/livehover/test/ComponentInjectionsHoverProviderTest.java +++ b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/livehover/test/ComponentInjectionsHoverProviderTest.java @@ -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; diff --git a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/project/harness/MockRequestMapping.java b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/project/harness/MockRequestMapping.java index 110ffe6aa..2e63ccb46 100644 --- a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/project/harness/MockRequestMapping.java +++ b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/project/harness/MockRequestMapping.java @@ -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; diff --git a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/project/harness/MockRunningAppProvider.java b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/project/harness/MockRunningAppProvider.java index 86ce0791d..d656aa291 100644 --- a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/project/harness/MockRunningAppProvider.java +++ b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/project/harness/MockRunningAppProvider.java @@ -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 requestMappings = LocalSpringBootApp.parseRequestMappingsJson(mappings, "1.x"); + Collection requestMappings = LocalSpringBootApp.parseRequestMappingsJson(mappings, "1.x"); when(app.getRequestMappings()).thenReturn(requestMappings); return this; } - public MockAppBuilder requestMappings(Collection rms) throws Exception { + public MockAppBuilder requestMappings(Collection rms) throws Exception { when(app.getRequestMappings()).thenReturn(rms); return this; }