From 46e665c388a89e40c4f75f483ea8a6b35686d645 Mon Sep 17 00:00:00 2001 From: jmatsuok Date: Tue, 14 Apr 2026 20:41:28 -0400 Subject: [PATCH] Add support for querying mbean attributes --- .../io/cryostat/core/net/JFRConnection.java | 7 + .../cryostat/core/net/JFRJMXConnection.java | 34 +++++ .../libcryostat/net/MbeanAttributeMap.java | 130 ++++++++++++++++++ 3 files changed, 171 insertions(+) create mode 100644 libcryostat/src/main/java/io/cryostat/libcryostat/net/MbeanAttributeMap.java diff --git a/cryostat-core/src/main/java/io/cryostat/core/net/JFRConnection.java b/cryostat-core/src/main/java/io/cryostat/core/net/JFRConnection.java index 6c763d4f..9d044909 100644 --- a/cryostat-core/src/main/java/io/cryostat/core/net/JFRConnection.java +++ b/cryostat-core/src/main/java/io/cryostat/core/net/JFRConnection.java @@ -33,6 +33,7 @@ import io.cryostat.libcryostat.JvmIdentifier; import io.cryostat.libcryostat.net.IDException; import io.cryostat.libcryostat.net.MBeanMetrics; +import io.cryostat.libcryostat.net.MbeanAttributeMap; import io.cryostat.libcryostat.sys.Clock; import io.cryostat.libcryostat.triggers.SmartTrigger; @@ -87,6 +88,12 @@ public default void disableSmartTrigger(String definitions) throws ConnectionExc throw new ConnectionException("Unimplemented"); } + public List queryMbeanAttributes() + throws IOException, + InstanceNotFoundException, + IntrospectionException, + ReflectionException; + public MBeanMetrics getMBeanMetrics() throws ConnectionException, IOException, diff --git a/cryostat-core/src/main/java/io/cryostat/core/net/JFRJMXConnection.java b/cryostat-core/src/main/java/io/cryostat/core/net/JFRJMXConnection.java index 7eb06a6b..c23ad43f 100644 --- a/cryostat-core/src/main/java/io/cryostat/core/net/JFRJMXConnection.java +++ b/cryostat-core/src/main/java/io/cryostat/core/net/JFRJMXConnection.java @@ -31,7 +31,9 @@ import javax.management.IntrospectionException; import javax.management.MBeanAttributeInfo; import javax.management.MBeanException; +import javax.management.MBeanInfo; import javax.management.MalformedObjectNameException; +import javax.management.ObjectInstance; import javax.management.ObjectName; import javax.management.ReflectionException; import javax.management.openmbean.CompositeData; @@ -62,6 +64,8 @@ import io.cryostat.libcryostat.JvmIdentifier; import io.cryostat.libcryostat.net.IDException; import io.cryostat.libcryostat.net.MBeanMetrics; +import io.cryostat.libcryostat.net.MbeanAttributeMap; +import io.cryostat.libcryostat.net.MbeanAttributeMap.MBeanAttribute; import io.cryostat.libcryostat.net.MemoryMetrics; import io.cryostat.libcryostat.net.OperatingSystemMetrics; import io.cryostat.libcryostat.net.RuntimeMetrics; @@ -428,4 +432,34 @@ public ConnectionFailureException(String message) { super(message); } } + + @Override + public synchronized List queryMbeanAttributes() + throws IOException, + InstanceNotFoundException, + IntrospectionException, + ReflectionException { + if (!isConnected()) { + connect(); + } + List attributeMap = new ArrayList(); + // null,null returns all Mbeans + Set beans = rjmxConnection.getMBeanServer().queryMBeans(null, null); + for (ObjectInstance bean : beans) { + List attrs = new ArrayList<>(); + MBeanInfo info = rjmxConnection.getMBeanServer().getMBeanInfo(bean.getObjectName()); + for (MBeanAttributeInfo a : info.getAttributes()) { + attrs.add( + new MBeanAttribute( + a.getName(), + a.getType(), + a.getDescription(), + bean.getClassName(), + a.isReadable(), + a.isWritable())); + } + attributeMap.add(new MbeanAttributeMap(bean.getClassName(), attrs)); + } + return attributeMap; + } } diff --git a/libcryostat/src/main/java/io/cryostat/libcryostat/net/MbeanAttributeMap.java b/libcryostat/src/main/java/io/cryostat/libcryostat/net/MbeanAttributeMap.java new file mode 100644 index 00000000..fea57a60 --- /dev/null +++ b/libcryostat/src/main/java/io/cryostat/libcryostat/net/MbeanAttributeMap.java @@ -0,0 +1,130 @@ +/* + * Copyright The Cryostat Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.cryostat.libcryostat.net; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class MbeanAttributeMap { + + private String mBeanName; + private List attributes; + + public MbeanAttributeMap(String name, List attrs) { + this.mBeanName = name; + this.attributes = new ArrayList(attrs); + } + + // Default Constructor for ObjectMapper deserialization + public MbeanAttributeMap() { + this("", Collections.emptyList()); + } + + public String getMbeanName() { + return mBeanName; + } + + public List getAttributes() { + return Collections.unmodifiableList(attributes); + } + + public void setMbeanName(String mbeanName) { + this.mBeanName = mbeanName; + } + + public void setAttributes(List attributes) { + this.attributes = new ArrayList(attributes); + } + + public static class MBeanAttribute { + + private String name; + private String type; + private String description; + private String parentBean; + private boolean isReadable; + private boolean isWritable; + + public MBeanAttribute( + String name, + String type, + String description, + String parentBean, + boolean isReadable, + boolean isWritable) { + this.name = name; + this.type = type; + this.description = description; + this.parentBean = parentBean; + this.isReadable = isReadable; + this.isWritable = isWritable; + } + + // Default constructor for ObjectMapper deserialization + public MBeanAttribute() { + this("", "", "", "", false, false); + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getParentBean() { + return parentBean; + } + + public void setParentBean(String parentBean) { + this.parentBean = parentBean; + } + + public boolean isReadable() { + return isReadable; + } + + public void setIsReadable(boolean isReadable) { + this.isReadable = isReadable; + } + + public boolean isWritable() { + return isWritable; + } + + public void setIsWritable(boolean isWritable) { + this.isWritable = isWritable; + } + } +}