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 acc8e473c..294edb99f 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 @@ -78,6 +78,8 @@ public abstract class AbstractSpringBootApp implements SpringBootApp { private String jmxMbeanActuatorDomain; private Set nonBootLiveMBeanNames; + private Boolean hasJmxBeans; + private int retryCount; private LiveBeansModel cachedBeansModel; private String cachedBeansModelMD5; @@ -105,12 +107,6 @@ public abstract class AbstractSpringBootApp implements SpringBootApp { @Override public abstract String getProcessName() throws Exception; - @Override - public abstract boolean isSpringBootApp(); - - @Override - public abstract boolean isSpringApp(); - private final MemoizingDisposableSupplier jmxConnector = new MemoizingDisposableSupplier( //creating jmx connector: @@ -158,6 +154,11 @@ public abstract class AbstractSpringBootApp implements SpringBootApp { jmxConnector.dispose(); } + public boolean containsSystemProperty(Object key) throws Exception { + Properties props = getSystemProperties(); + return props.containsKey(key); + } + @Override public List getActiveProfiles() { try { @@ -228,7 +229,63 @@ public abstract class AbstractSpringBootApp implements SpringBootApp { } @Override - public boolean providesNonBootLiveBeans() { + public boolean hasUsefulJmxBeans() { + if (hasJmxBeans == null) { + try { + if (containsSystemProperty("sts4.languageserver.name")) { + logger.info("language server process found -- " + this.getProcessID() + " = " + getProcessName()); + hasJmxBeans = Boolean.FALSE; + } + else { + logger.info("check for spring jmx beans (retry no. " + retryCount + ") -- " + this.getProcessID() + " = " + getProcessName()); + + boolean jmxBeansFound = containsSpringJmxBeans(); + if (jmxBeansFound) { + hasJmxBeans = Boolean.TRUE; + logger.info("spring jmx beans found -- " + this.getProcessID() + " = " + getProcessName()); + } + else if (retryCount == 3) { + hasJmxBeans = Boolean.FALSE; + logger.info("no spring jmx beans found after trying 4 times -- " + this.getProcessID() + " = " + getProcessName()); + } + else { + retryCount++; + } + } + } + catch (Exception e) { + if (retryCount == 3) { + hasJmxBeans = Boolean.FALSE; + + try { + logger.info("no spring jmx beans found after trying 4 times -- " + this.getProcessID() + " = " + getProcessName()); + } catch (Exception e1) { + logger.info("no spring jmx beans found after trying 4 times -- " + this.getProcessID() + " = (process name unknown)"); + } + } + else { + retryCount++; + } + } + } + + return hasJmxBeans != null ? hasJmxBeans : false; + } + + private boolean containsSpringJmxBeans() throws Exception { + return withTimeout(() -> withJmxConnector(jmxConnector -> { + MBeanServerConnection connection = jmxConnector.getMBeanServerConnection(); + + 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); + return names != null && names.size() > 0; + })); + } + + protected boolean providesNonBootLiveBeans() { return getNonBootSpringLiveMBeans().size() > 0; } diff --git a/headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/LocalSpringBootApp.java b/headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/LocalSpringBootApp.java index 7d3f713df..a61cbc269 100644 --- a/headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/LocalSpringBootApp.java +++ b/headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/LocalSpringBootApp.java @@ -37,9 +37,6 @@ public class LocalSpringBootApp extends AbstractSpringBootApp { private static final String LOCAL_CONNECTOR_ADDRESS = "com.sun.management.jmxremote.localConnectorAddress"; - private Boolean isSpringBootApp; - private Boolean isSpringApp; - private static LocalSpringBootAppCache cache = new LocalSpringBootAppCache(); public static Collection getAllRunningJavaApps() throws Exception { @@ -47,7 +44,7 @@ public class LocalSpringBootApp extends AbstractSpringBootApp { } public static Collection getAllRunningSpringApps() throws Exception { - return getAllRunningJavaApps().stream().filter(app -> app.isSpringBootApp() || app.isSpringApp()).collect(CollectorUtil.toImmutableList()); + return getAllRunningJavaApps().parallelStream().filter(app -> app.hasUsefulJmxBeans()).collect(CollectorUtil.toImmutableList()); } public LocalSpringBootApp(VirtualMachineDescriptor vmd) throws AttachNotSupportedException, IOException { @@ -92,57 +89,6 @@ public class LocalSpringBootApp extends AbstractSpringBootApp { return firstSpace < 0 ? rawName : rawName.substring(0, firstSpace); } - @Override - public boolean isSpringApp() { - if (isSpringApp == null) { - try { - isSpringApp = !containsSystemProperty("sts4.languageserver.name") - && ( - isSpringAppClasspath() || - providesNonBootLiveBeans() - ); - } catch (Exception e) { - //Couldn't determine if the VM is a spring boot app. Could be it already died. Or could be its not accessible (yet). - // We will ignore the exception, pretend its not a boot app (most likely isn't) but DO NOT CACHE this result - // so it will be retried again on the next polling loop. - return false; - } - } - return isSpringApp; - } - - private boolean isSpringAppClasspath() throws Exception { - return contains(getClasspath(), "spring-core"); - } - - @Override - public boolean isSpringBootApp() { - if (isSpringBootApp == null) { - try { - isSpringBootApp = !containsSystemProperty("sts4.languageserver.name") - && ( - isSpringBootAppClasspath() || - isSpringBootAppSysprops() - ); - } catch (Exception e) { - //Couldn't determine if the VM is a spring boot app. Could be it already died. Or could be its not accessible (yet). - // We will ignore the exception, pretend its not a boot app (most likely isn't) but DO NOT CACHE this result - // so it will be retried again on the next polling loop. - return false; - } - } - return isSpringBootApp; - } - - private boolean isSpringBootAppSysprops() throws Exception { - Properties sysprops = getSystemProperties(); - return "org.springframework.boot.loader".equals(sysprops.getProperty("java.protocol.handler.pkgs")); - } - - private boolean isSpringBootAppClasspath() throws Exception { - return contains(getClasspath(), "spring-boot"); - } - @Override public Properties getSystemProperties() throws Exception { try { @@ -153,11 +99,6 @@ public class LocalSpringBootApp extends AbstractSpringBootApp { } } - public boolean containsSystemProperty(Object key) throws Exception { - Properties props = getSystemProperties(); - return props.containsKey(key); - } - protected boolean contains(String[] cpElements, String element) { for (String cpElement : cpElements) { if (cpElement.contains(element)) { diff --git a/headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/RemoteSpringBootApp.java b/headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/RemoteSpringBootApp.java index 90339638b..7530e00de 100644 --- a/headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/RemoteSpringBootApp.java +++ b/headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/RemoteSpringBootApp.java @@ -62,23 +62,6 @@ public class RemoteSpringBootApp extends AbstractSpringBootApp { }); } - @Override - public boolean isSpringBootApp() { - return isSpringApp(); - } - - @Override - public boolean isSpringApp() { - //For now, let's assume that, if its not a boot app, then we won't create a RemoteSpringBootApp instance for it. - //The check that is here really only determines whether there's a process reachable at the remote jmx url. - return getProcessID() != null; - } - - @Override - public boolean providesNonBootLiveBeans() { - return false; - } - @Override public String getProcessID() { try { 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 cb6cca2f4..533ff3d67 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 @@ -30,14 +30,12 @@ public interface SpringBootApp extends Disposable { String getPort() throws Exception; String getContextPath() throws Exception; - boolean isSpringBootApp(); - boolean isSpringApp(); + boolean hasUsefulJmxBeans(); String getEnvironment() throws Exception; Collection getRequestMappings() throws Exception; LiveBeansModel getBeans(); - boolean providesNonBootLiveBeans(); List getActiveProfiles(); Optional> getLiveConditionals() throws Exception; 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 681bc424a..2b43ea775 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 @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2017 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,7 +20,7 @@ public class SpringBootAppCLI { public static void main(String[] args) throws Exception { Collection allRunningJavaApps = LocalSpringBootApp.getAllRunningJavaApps(); for (SpringBootApp app : allRunningJavaApps) { - if (app.isSpringBootApp()) { + if (app.hasUsefulJmxBeans()) { printBootAppDetails(app); } } 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 bddecef50..a8ba0fc75 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 @@ -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 @@ -96,7 +96,7 @@ public class RemoteRunningAppsProvider implements RunningAppProvider { @Override public synchronized Collection getAllRunningSpringApps() throws Exception { - return remoteAppInstances.values().stream().filter(SpringBootApp::isSpringBootApp).collect(CollectorUtil.toImmutableList()); + return remoteAppInstances.values().stream().filter(SpringBootApp::hasUsefulJmxBeans).collect(CollectorUtil.toImmutableList()); } synchronized void handleSettings(Settings settings) { 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 eb97d9ca0..87b19288b 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 @@ -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 @@ -109,7 +109,7 @@ public class MockRunningAppProvider { } public MockAppBuilder isSpringBootApp(boolean isBoot) throws Exception { - when(app.isSpringBootApp()).thenReturn(isBoot); + when(app.hasUsefulJmxBeans()).thenReturn(isBoot); return this; }