changed live hover checks from various classpath and property checks towards JMX bean lookup, and turned the check into parallel checks
This commit is contained in:
@@ -78,6 +78,8 @@ public abstract class AbstractSpringBootApp implements SpringBootApp {
|
||||
private String jmxMbeanActuatorDomain;
|
||||
|
||||
private Set<ObjectName> 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> jmxConnector = new MemoizingDisposableSupplier<JMXConnector>(
|
||||
//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<String> 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<ObjectName> names = connection.queryNames(null, queryExp);
|
||||
return names != null && names.size() > 0;
|
||||
}));
|
||||
}
|
||||
|
||||
protected boolean providesNonBootLiveBeans() {
|
||||
return getNonBootSpringLiveMBeans().size() > 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -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<SpringBootApp> getAllRunningJavaApps() throws Exception {
|
||||
@@ -47,7 +44,7 @@ public class LocalSpringBootApp extends AbstractSpringBootApp {
|
||||
}
|
||||
|
||||
public static Collection<SpringBootApp> 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)) {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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<RequestMapping> getRequestMappings() throws Exception;
|
||||
|
||||
LiveBeansModel getBeans();
|
||||
boolean providesNonBootLiveBeans();
|
||||
|
||||
List<String> getActiveProfiles();
|
||||
Optional<List<LiveConditional>> getLiveConditionals() throws Exception;
|
||||
|
||||
@@ -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<SpringBootApp> allRunningJavaApps = LocalSpringBootApp.getAllRunningJavaApps();
|
||||
for (SpringBootApp app : allRunningJavaApps) {
|
||||
if (app.isSpringBootApp()) {
|
||||
if (app.hasUsefulJmxBeans()) {
|
||||
printBootAppDetails(app);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<SpringBootApp> 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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user