Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

8284: Fix Jolokia discovery issues #597

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -34,48 +34,30 @@
package org.openjdk.jmc.jolokia;

import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

import javax.management.Attribute;
import javax.management.AttributeList;
import javax.management.InstanceNotFoundException;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
import javax.management.openmbean.CompositeDataSupport;
import javax.management.openmbean.TabularDataSupport;
import javax.management.remote.JMXServiceURL;

import org.jolokia.client.jmxadapter.RemoteJmxAdapter;
import org.openjdk.jmc.common.jvm.Connectable;
import org.openjdk.jmc.common.jvm.JVMArch;
import org.openjdk.jmc.common.jvm.JVMDescriptor;
import org.openjdk.jmc.common.jvm.JVMType;

/**
* Provide data about JVMs accessed over Jolokia for the JVM browser
*/
public class JolokiaAgentDescriptor implements ServerConnectionDescriptor {

public static final JVMDescriptor NULL_DESCRIPTOR = new JVMDescriptor(null, null, null, null, null, null, null,
null, false, Connectable.UNKNOWN);
private final JMXServiceURL serviceUrl;
private final Map<String, ?> agentData;
private final JVMDescriptor jvmDescriptor;

public JolokiaAgentDescriptor(Map<String, ?> agentData, JVMDescriptor jvmDescriptor)
throws URISyntaxException, MalformedURLException {
public JolokiaAgentDescriptor(Map<String, ?> agentData) throws URISyntaxException, MalformedURLException {
super();
URI uri = new URI((String) agentData.get("url")); //$NON-NLS-1$
this.serviceUrl = new JMXServiceURL(
String.format("service:jmx:jolokia://%s:%s%s", uri.getHost(), uri.getPort(), uri.getPath())); //$NON-NLS-1$
this.agentData = agentData;
this.jvmDescriptor = jvmDescriptor;
}

JMXServiceURL getServiceUrl() {
Expand All @@ -94,86 +76,10 @@ public String getDisplayName() {

@Override
public JVMDescriptor getJvmInfo() {
return this.jvmDescriptor;
}

/**
* Best effort to extract JVM information from a connection if everything works. Can be adjusted
* to support different flavors of JVM.
*/
public static JVMDescriptor attemptToGetJvmInfo(RemoteJmxAdapter adapter) {
try {
AttributeList attributes = adapter.getAttributes(new ObjectName(ManagementFactory.RUNTIME_MXBEAN_NAME),
new String[] {"Pid", "Name", "InputArguments", "SystemProperties"}); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
Integer pid = null;
String arguments = null, javaCommand = null, javaVersion = null, vmName = null, vmVendor = null;
boolean isDebug = false;
JVMType type = JVMType.UNKNOWN;
JVMArch arch = JVMArch.UNKNOWN;
for (Attribute attribute : attributes.asList()) {
// newer JVM have pid as separate attribute, older have to parse from name
if (attribute.getName().equalsIgnoreCase("Pid")) { //$NON-NLS-1$
try {
pid = Integer.valueOf(String.valueOf(attribute.getValue()));
} catch (NumberFormatException ignore) {
}
} else if (attribute.getName().equalsIgnoreCase("Name") && pid == null) { //$NON-NLS-1$
String pidAndHost = String.valueOf(attribute.getValue());
int separator = pidAndHost.indexOf('@');
if (separator > 0) {
try {
pid = Integer.valueOf(pidAndHost.substring(0, separator));
} catch (NumberFormatException e) {
}
}
} else if (attribute.getName().equalsIgnoreCase("InputArguments")) { //$NON-NLS-1$

if (attribute.getValue() instanceof String[]) {
arguments = Arrays.toString((String[]) attribute.getValue());
} else {
arguments = String.valueOf(attribute.getValue());
}
if (arguments.contains("-agentlib:jdwp")) { //$NON-NLS-1$
isDebug = true;
}
} else if (attribute.getName().equalsIgnoreCase("SystemProperties") //$NON-NLS-1$
&& attribute.getValue() instanceof TabularDataSupport) {
TabularDataSupport systemProperties = (TabularDataSupport) attribute.getValue();

// quite clumsy: iterate over properties as we need to use the exact key, which is non trivial
// to reproduce
for (Object entry : systemProperties.values()) {
String key = ((CompositeDataSupport) entry).get("key").toString(); //$NON-NLS-1$
String value = ((CompositeDataSupport) entry).get("value").toString(); //$NON-NLS-1$
if (key.equalsIgnoreCase("sun.management.compiler")) { //$NON-NLS-1$
if (value.toLowerCase().contains("hotspot")) { //$NON-NLS-1$
type = JVMType.HOTSPOT;
}
} else if (key.equalsIgnoreCase("sun.arch.data.model")) { //$NON-NLS-1$
String archIndicator = value;
if (archIndicator.contains("64")) { //$NON-NLS-1$
arch = JVMArch.BIT64;
} else if (archIndicator.contains("32")) { //$NON-NLS-1$
arch = JVMArch.BIT32;
}
} else if (key.equalsIgnoreCase("sun.java.command")) { //$NON-NLS-1$
javaCommand = value;
} else if (key.equalsIgnoreCase("java.version")) { //$NON-NLS-1$
javaVersion = value;
} else if (key.equalsIgnoreCase("java.vm.name")) { //$NON-NLS-1$
vmName = value;
} else if (key.equalsIgnoreCase("java.vm.vendor")) { //$NON-NLS-1$
vmVendor = value;
}
}
}
}
return new JVMDescriptor(javaVersion, type, arch, javaCommand, arguments, vmName, vmVendor, pid, isDebug,
Connectable.UNKNOWN);

} catch (RuntimeException | IOException | InstanceNotFoundException | MalformedObjectNameException ignore) {
return NULL_DESCRIPTOR;
}
//Note: From discovery we may know a bit about the target JVM, however the presence of JVM info
//is interpreted as a local JVM by AgentJmxHelper.isLocalJvm() hence the JMC Agent will be available
//which does not make sense over this protocol
return null;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,7 @@
import java.util.HashMap;
import java.util.Map;

import org.jolokia.client.jmxadapter.RemoteJmxAdapter;
import org.jolokia.service.discovery.JolokiaDiscovery;
import org.openjdk.jmc.common.jvm.JVMDescriptor;
import org.openjdk.jmc.jolokia.preferences.PreferenceConstants;

/**
Expand All @@ -49,10 +47,13 @@
*/
public class JolokiaDiscoveryListener extends AbstractCachedDescriptorProvider implements PreferenceConstants {

JolokiaDiscoverySettings settings;
private JolokiaDiscoverySettings settings;
private final JolokiaDiscovery jolokiaDiscovery;

public JolokiaDiscoveryListener(JolokiaDiscoverySettings settings) {
this.settings = settings;
jolokiaDiscovery = new JolokiaDiscovery();
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ensure that only one instance is instantiated (linked to the lifecycle of this plugin). If one tries to instantiate another, it will attempt to register an MBean and sinply crash

jolokiaDiscovery.init(this.settings.getJolokiaContext());
}

public JolokiaDiscoveryListener() {
Expand All @@ -66,22 +67,13 @@ protected Map<String, ServerConnectionDescriptor> discoverJvms() {
return found;
}
try {
JolokiaDiscovery jolokiaDiscovery = new JolokiaDiscovery();
jolokiaDiscovery.init(this.settings.getJolokiaContext());
for (Object object : jolokiaDiscovery.lookupAgentsWithTimeoutAndMulticastAddress(
this.settings.getDiscoveryTimeout(), this.settings.getMulticastGroup(),
this.settings.getMulticastPort())) {
try {
@SuppressWarnings("unchecked")
Map<String, ?> response = (Map<String, ?>) object;
JVMDescriptor jvmInfo;
try {// if it is connectable, see if we can get info from connection
jvmInfo = JolokiaAgentDescriptor
.attemptToGetJvmInfo(new RemoteJmxAdapter(String.valueOf(response.get("url")))); //$NON-NLS-1$
} catch (Exception ignore) {
jvmInfo = JolokiaAgentDescriptor.NULL_DESCRIPTOR;
}
JolokiaAgentDescriptor agentDescriptor = new JolokiaAgentDescriptor(response, jvmInfo);
JolokiaAgentDescriptor agentDescriptor = new JolokiaAgentDescriptor(response);
found.put(agentDescriptor.getGUID(), agentDescriptor);

} catch (URISyntaxException ignore) {
Expand Down