/** * Helios, OpenSource Monitoring * Brought to you by the Helios Development Group * * Copyright 2007, Helios Development Group and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. * */ package org.helios.apmrouter.jmx; import org.helios.apmrouter.util.StringHelper; import javax.management.*; import javax.management.openmbean.CompositeData; import javax.management.remote.*; import java.beans.BeanInfo; import java.beans.Introspector; import java.io.IOException; import java.lang.management.ManagementFactory; import java.net.InetAddress; import java.net.ServerSocket; import java.net.Socket; import java.rmi.registry.LocateRegistry; import java.rmi.server.RMISocketFactory; import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * <p>Title: JMXHelper</p> * <p>Description: Static JMX Utility methods</p> * <p>Company: Helios Development Group LLC</p> * @author Whitehead (nwhitehead AT heliosdev DOT org) * <p><code>org.helios.apmrouter.jmx.JMXHelper</code></p> */ public class JMXHelper { /** The property name where the jmx default domain is referenced */ public static final String JMX_DOMAIN_PROPERTY = "org.helios.jmx.domain"; /** The default jmx default domain is referenced */ public static final String JMX_DOMAIN_DEFAULT = System.getProperty(JMX_DOMAIN_PROPERTY, ManagementFactory.getPlatformMBeanServer().getDefaultDomain()); /** Regex WildCard Support Pattern for ObjectName key values */ public static final Pattern OBJECT_NAME_KP_WILDCARD = Pattern.compile("[:|,](\\S+?)~=\\[(\\S+?)\\]"); /** An object name filter that maps to all registered MBeans */ public static final ObjectName ALL_MBEANS_FILTER = objectName("*:*"); /** * Acquires the configured or default Helios target MBeanServer. * @return An MBeanServer. */ public static MBeanServer getHeliosMBeanServer() { MBeanServer server = null; String jmxDomain = ConfigurationHelper.getEnvThenSystemProperty(JMX_DOMAIN_PROPERTY, null); if(jmxDomain!=null) { server = getLocalMBeanServer(jmxDomain, true); } if(server==null) { return ManagementFactory.getPlatformMBeanServer(); } return server; } /** * Returns an array of matching ObjectNames * @param server The MBeanServer to query * @param pattern The ObjectName pattern * @param query An optional query expression * @return an array of ObjectNames */ public static ObjectName[] query(MBeanServerConnection server, ObjectName pattern, QueryExp query) { try { if(server==null) server = getHeliosMBeanServer(); Set<ObjectName> ons = server.queryNames(pattern, query); return ons.toArray(new ObjectName[ons.size()]); } catch (Exception e) { throw new RuntimeException("Failed to issue MBean query", e); } } /** * Returns an array of matching ObjectNames * @param server The MBeanServer to query * @param pattern The ObjectName pattern * @param query An optional query expression * @return an array of ObjectNames */ public static ObjectName[] query(MBeanServerConnection server, CharSequence pattern, QueryExp query) { return query(server, objectName(pattern), query); } /** * Returns an array of matching ObjectNames * @param server The MBeanServer to query * @param pattern The ObjectName pattern * @return an array of ObjectNames */ public static ObjectName[] query(MBeanServerConnection server, CharSequence pattern) { return query(server, objectName(pattern), null); } /** * Returns an array of matching ObjectNames * @param server The MBeanServer to query * @param pattern The ObjectName pattern * @return an array of ObjectNames */ public static ObjectName[] query(MBeanServerConnection server, ObjectName pattern) { return query(server, pattern, null); } /** * Returns an array of matching ObjectNames from the default MBeanServer * @param pattern The ObjectName pattern * @param query An optional query expression * @return an array of ObjectNames */ public static ObjectName[] query(ObjectName pattern, QueryExp query) { return query(getHeliosMBeanServer(), pattern, query); } /** * Returns an array of matching ObjectNames from the default MBeanServer * @param pattern The ObjectName pattern * @param query An optional query expression * @return an array of ObjectNames */ public static ObjectName[] query(CharSequence pattern, QueryExp query) { return query(getHeliosMBeanServer(), objectName(pattern), query); } /** * Returns an array of matching ObjectNames from the default MBeanServer * @param pattern The ObjectName pattern * @return an array of ObjectNames */ public static ObjectName[] query(ObjectName pattern) { return query(getHeliosMBeanServer(), pattern, null); } /** * Returns an array of matching ObjectNames from the default MBeanServer * @param pattern The ObjectName pattern * @return an array of ObjectNames */ public static ObjectName[] query(CharSequence pattern) { return query(getHeliosMBeanServer(), objectName(pattern), null); } /** * Determines if the passed Object is or represents a JMX ObjectName * @param obj the object to test * @return true if the passed Object is or represents a JMX ObjectName, false otherwise */ public static boolean isObjectName(Object obj) { if(obj==null) return false; if(obj instanceof ObjectName) return true; try { new ObjectName(obj.toString()); return true; } catch (Exception e) { return false; } } /** * Returns an MBeanConnection for an in-vm MBeanServer that has the specified default domain. * @param domain The default domain of the requested MBeanServer. * @return The located MBeanServerConnection or null if one cannot be located. */ public static MBeanServer getLocalMBeanServer(String domain) { return getLocalMBeanServer(domain, true); } /** * Searches for a matching MBeanServer in the passed list of domains and returns the first located. * If one cannot be located a null will be returned. * @param domains The default domain of the requested MBeanServer. * @return The located MBeanServerConnection or null if one cannot be found. */ public static MBeanServer getLocalMBeanServer(String...domains) { return getLocalMBeanServer(true, domains); } /** * Searches for a matching MBeanServer in the passed list of domains and returns the first located. * If one cannot be located, returnNullIfNotFound will either cause a null to be returned, or a RuntimeException. * @param returnNullIfNotFound If true, returns a null if a matching MBeanServer cannot be found. Otherwise, throws a RuntimeException. * @param domains The default domain of the requested MBeanServer. * @return The located MBeanServerConnection or null if one cannot be found and returnNullIfNotFound is true. */ public static MBeanServer getLocalMBeanServer(boolean returnNullIfNotFound, String...domains) { MBeanServer server = null; StringBuilder buff = new StringBuilder(); for(String domain: domains) { server = getLocalMBeanServer(domain); buff.append(domain).append(","); if(server!=null) return server; } if(returnNullIfNotFound) { return null; } throw new RuntimeException("No MBeanServer located for domains [" + buff.toString() + "]"); } /** * Returns an MBeanConnection for an in-vm MBeanServer that has the specified default domain. * @param domain The default domain of the requested MBeanServer. * @param returnNullIfNotFound If true, returns a null if a matching MBeanServer cannot be found. Otherwise, throws a RuntimeException. * @return The located MBeanServerConnection or null if one cannot be found and returnNullIfNotFound is true. */ public static MBeanServer getLocalMBeanServer(String domain, boolean returnNullIfNotFound) { if(domain==null || domain.equals("") || domain.equalsIgnoreCase("DefaultDomain") || domain.equalsIgnoreCase("Default")) { return ManagementFactory.getPlatformMBeanServer(); } List<MBeanServer> servers = MBeanServerFactory.findMBeanServer(null); for(MBeanServer server: servers) { if(server.getDefaultDomain().equals(domain)) return server; } if(returnNullIfNotFound) { return null; } throw new RuntimeException("No MBeanServer located for domain [" + domain + "]"); } /** * Acquires a connected JMX connection * @param jmxUrl The JMXServiceURL of the service to connec to * @return a JMXConnector */ public static JMXConnector getJMXConnection(CharSequence jmxUrl) { return getJMXConnection(jmxUrl, true, null); } /** * Acquires a JMX connection * @param jmxUrl The JMXServiceURL of the service to connec to * @param connect If true, the returned connector will be connected * @param environment a set of attributes to determine how the connection is made. Can be null. * @return a JMXConnector */ public static JMXConnector getJMXConnection(CharSequence jmxUrl, boolean connect, Map<String,?> environment) { if(jmxUrl==null) throw new IllegalArgumentException("The passed JMXServiceURL was null", new Throwable()); try { JMXConnector connector = JMXConnectorFactory.newJMXConnector(new JMXServiceURL(jmxUrl.toString().trim()), environment); if(connect) { connector.connect(); } return connector; } catch (Exception e) { e.printStackTrace(System.err); throw new RuntimeException("Failed to acquire JMXConnection to [" + jmxUrl + "]", e); } } /** * Creates a new JMX object name. * @param on A string type representing the ObjectName string. * @return an ObjectName the created ObjectName */ public static ObjectName objectName(CharSequence on) { try { return new ObjectName(on.toString().trim()); } catch (Exception e) { throw new RuntimeException("Failed to create Object Name", e); } } /** * Creates a new JMX object name. * @param on An object representing the ObjectName * @return an ObjectName the created ObjectName */ public static ObjectName objectName(Object on) { try { return new ObjectName(on.toString().trim()); } catch (Exception e) { throw new RuntimeException("Failed to create Object Name", e); } } /** * Creates a new JMX object name by appending properties on the end of an existing name * @param on An existing ObjectName * @param props Appended properties in the for {@code key=value} * @return an ObjectName the created ObjectName */ public static ObjectName objectName(ObjectName on, CharSequence...props) { StringBuilder b = new StringBuilder(on.toString()); try { if(props!=null) { for(CharSequence prop: props) { b.append(",").append(prop); } } return new ObjectName(b.toString()); } catch (Exception e) { throw new RuntimeException("Failed to create Object Name from [" + b + "]", e); } } /** * Creates a new JMX object name. * @param domain A string type representing the ObjectName domain * @param properties A hash table of the Object name's properties * @return an ObjectName the created ObjectName */ public static ObjectName objectName(CharSequence domain, Hashtable<String, String> properties) { try { return new ObjectName(domain.toString(), properties); } catch (Exception e) { throw new RuntimeException("Failed to create Object Name", e); } } /** * Creates a new JMX object name. * @param domain The ObjectName domain * @param nameValuePairs an (even lengthed) array of name value pairs making up the key properties * @return an ObjectName the created ObjectName */ public static ObjectName objectName(CharSequence domain, CharSequence...nameValuePairs) { if(domain==null || domain.toString().length()<1) throw new IllegalArgumentException("Null or zero length domain name"); if(nameValuePairs==null || nameValuePairs.length<1 || nameValuePairs.length%2!=0) { throw new IllegalArgumentException("Invalid number of namevaluepairs [" + (nameValuePairs==null ? 0 : nameValuePairs.length) + "]"); } try { Hashtable<String, String> props = new Hashtable<String, String>(); for(int i = 0; i < nameValuePairs.length; i++) { if(nameValuePairs[i]==null || nameValuePairs[i].toString().length()<1) { throw new IllegalArgumentException("Null or blank nameValuePair entry at index [" + i + "]"); } String key = nameValuePairs[i].toString(); i++; if(nameValuePairs[i]==null || nameValuePairs[i].toString().length()<1) { throw new IllegalArgumentException("Null or blank nameValuePair entry at index [" + i + "]"); } String value = nameValuePairs[i].toString(); props.put(key, value); } return new ObjectName(domain.toString(), props); } catch (IllegalArgumentException iae) { throw iae; } catch (Exception e) { throw new RuntimeException("Failed to create Object Name", e); } } /** * Registers an MBean * @param server The MBeanServer to register in * @param objectName The ObjectName of the MBean * @param mbean The MBean object instance to register */ public static void registerMBean(MBeanServer server, ObjectName objectName, Object mbean) { try { server.registerMBean(mbean, objectName); } catch(Exception e) { //throw new RuntimeException("Failed to register MBean [" + objectName + "]", e); System.err.println("Failed to register MBean [" + objectName + "]"); } } /** * Registers an MBean in the helios MBeanServer * @param objectName The ObjectName of the MBean * @param mbean The MBean object instance to register */ public static void registerMBean(ObjectName objectName, Object mbean) { registerMBean(getHeliosMBeanServer(), objectName, mbean); } /** * Unregisters the named MBean from the passed MBeanServer * @param server The MBeanServer to unregister from * @param objectName The ObjectName of the MBean to unregister */ public static void unregisterMBean(MBeanServer server, ObjectName objectName) { try { server.unregisterMBean(objectName); } catch(Exception e) { //throw new RuntimeException("Failed to register MBean [" + objectName + "]", e); System.err.println("Failed to unregister MBean [" + objectName + "]"); } } /** * Registers the named MBean in the passed MBeanServer * @param server The MBeanServer to register with * @param mbean The object to register * @param objectName The ObjectName of the MBean to register */ public static void registerMBean(MBeanServer server, Object mbean, ObjectName objectName) { try { server.registerMBean(mbean, objectName); } catch(Exception e) { throw new RuntimeException("Failed to register MBean [" + objectName + "]", e); //System.err.println("Failed to register MBean [" + objectName + "]:" + e); } } /** * Registers the named MBean in the helios MBeanServer * @param mbean The object to register * @param objectName The ObjectName of the MBean to register */ public static void registerMBean(Object mbean, ObjectName objectName) { registerMBean(getHeliosMBeanServer(), mbean, objectName); } /** * Unregisters the named MBean from the Helios MBeanServer * @param objectName The ObjectName of the MBean to unregister */ public static void unregisterMBean(ObjectName objectName) { unregisterMBean(getHeliosMBeanServer(), objectName); } /** * Retrieves MBeanInfo on the specified object name. * @param server The mbean server * @param on The object name * @return an MBeanInfo */ public static MBeanInfo mbeanInfo(MBeanServerConnection server, CharSequence on) { try { return server.getMBeanInfo(objectName(on)); } catch (Exception e) { throw new RuntimeException("Failed to get MBeanInfo", e); } } /** * Sets an MBean attribute. * @param on The object name * @param server The mbean server * @param name The attribute name * @param value The attribute value */ public static void setAttribute(CharSequence on, MBeanServerConnection server, String name, Object value) { try { server.setAttribute(objectName(on), new Attribute(name, value)); } catch (Exception e) { throw new RuntimeException("Failed to set Attribute", e); } } /** * Sets a list of MBean attributes. Throws no exceptions. Returns a map of successfully set values. * @param on on the object name * @param server the mbean server * @param attributes The attributes to set * @return a map of successfully set values */ public static Map<String, Object> setAttributesWithRet(CharSequence on, MBeanServerConnection server, Object...attributes) { Map<String, Object> returnValues = new HashMap<String, Object>(attributes.length); Collection<NVP> list = NVP.generate(attributes); for(NVP nvp: list) { try { setAttribute(on, server, nvp.getName(), nvp.getValue()); returnValues.put(nvp.getName(), nvp.getValue()); } catch (Exception e) {} } return returnValues; } /** * Returns a String->Object Map of the named attributes from the Mbean. * @param on The object name of the MBean. * @param server The MBeanServerConnection the MBean is registered in. If this is null, uses the helios mbean server * @param attributes An array of attribute names to retrieve. If this is null or empty, retrieves all the names * @return A name value map of the requested attributes. */ public static Map<String, Object> getAttributes(ObjectName on, MBeanServerConnection server, String...attributes) { try { if(attributes==null || attributes.length<1) { attributes = getAttributeNames(on, server); } Map<String, Object> attrs = new HashMap<String, Object>(attributes.length); AttributeList attributeList = server.getAttributes(on, attributes); for(int i = 0; i < attributeList.size(); i++) { Attribute at = (Attribute)attributeList.get(i); attrs.put(at.getName(), at.getValue()); } return attrs; } catch (Exception e) { throw new RuntimeException("Failed to getAttributes on [" + on + "]", e); } } /** * Returns a String->Object Map of the named attributes from the Mbean in the helios mbeanserver * @param on The object name of the MBean. * @param attributes An array of attribute names to retrieve. If this is null or empty, retrieves all the names * @return A name value map of the requested attributes. */ public static Map<String, Object> getAttributes(ObjectName on, String...attributes) { return getAttributes(on, getHeliosMBeanServer(), attributes); } /** * Returns an array of the names of the attributes for the passed ObjectName reached through the helios mbeanserver * @param objectName The mbean to get the attribute names for * @return an array of strings */ public static String[] getAttributeNames(ObjectName objectName) { return getAttributeNames(objectName, getHeliosMBeanServer()); } /** * Returns an array of the names of the attributes for the passed ObjectName reached through the passed mbean server connection * @param objectName The mbean to get the attribute names for * @param connection The connection to reach the mbean through. If null, uses the helios mbean server * @return an array of strings */ public static String[] getAttributeNames(ObjectName objectName, MBeanServerConnection connection) { if(objectName==null) throw new IllegalArgumentException("The passed objectname was null", new Throwable()); if(connection==null) connection = getHeliosMBeanServer(); try { MBeanAttributeInfo[] infos = connection.getMBeanInfo(objectName).getAttributes(); String[] names = new String[infos.length]; for(int i = 0; i < infos.length; i++) { names[i] = infos[i].getName(); } return names; } catch (Exception ex) { return new String[0]; } } /** * Inspects the array to see if it contains the passed string. * @param name The name to search for * @param array The array to search * @return true if the array contains the passed string. */ public static boolean isIn(String name, String[] array) { if(array==null || name==null) return false; return Arrays.binarySearch(array, name)>=0; } /** * Sets a list of MBean attributes. Throws an exception on any failure. Returns a map of successfully set values. * @param on the object name * @param server the mbean server * @param attributes The attributes to set * @return a map of successfully set values. */ public static Map<String, Object> setAttributes(CharSequence on, MBeanServerConnection server, Object...attributes) { Map<String, Object> returnValues = new HashMap<String, Object>(attributes.length); Collection<NVP> list = NVP.generate(attributes); for(NVP nvp: list) { setAttribute(on, server, nvp.getName(), nvp.getValue()); returnValues.put(nvp.getName(), nvp.getValue()); } return returnValues; } /** * Gets an attribute value from an mbean. * @param on on the object name * @param server the mbean server * @param name the name of the attribute * @return the value of the attribute */ public static Object getAttribute(ObjectName on, MBeanServerConnection server, String name) { try { return server.getAttribute(on,name); } catch (Exception e) { throw new RuntimeException("Failed to get attribute", e); } } /** * Invokes an operation on the mbean. * @param on the object name * @param server the mbean server * @param action The name of the operation to invoke * @param args The argument values to pass to the invocation * @param signature The argument signature * @return the return value of the invocation */ public static Object invoke(ObjectName on, MBeanServerConnection server, String action, Object[] args, String[] signature) { try { return server.invoke(on, action, args, signature); } catch (Exception e) { throw new RuntimeException("Failed to invoke operation", e); } } /** * Invokes an operation on the mbean. * @param on the object name * @param server the mbean server * @param action The name of the operation to invoke * @param args The argument values to pass to the invocation * @param signature The argument signature * @return the return value of the invocation */ public static Object invoke(CharSequence on, MBeanServerConnection server, String action, Object[] args, String[] signature) { return invoke(objectName(on), server, action, args, signature); } /** * Returns a set of ObjectNames matching the passed wildcard object names * @param wildcardEq The ObjectName equals * @param wildcardWc The ObjectName wildcard * @param conn The MBeanServer connection * @return a set of ObjectNames matching the passed wildcard object name */ public static Set<ObjectName> getMatchingObjectNames(CharSequence wildcardEq, CharSequence wildcardWc, MBeanServerConnection conn) { ObjectName wildcardEquals = objectName(wildcardEq); ObjectName wildcard = objectName(wildcardWc); final String wc = new StringBuilder("(").append(wildcardEquals).append("$)").toString(); Set<ObjectName> names = new HashSet<ObjectName>(); // A map of regex patterns to match on, keyed by the actual property key Map<String, Pattern> wildcardQueryProps = new HashMap<String, Pattern>(); // the original wildcard object's key properties Hashtable<String, String> wildcardProps = objectName(wildcard).getKeyPropertyList(); // the non wildcarded property keys we will query the mbean server with Hashtable<String, String> queryProps = new Hashtable<String, String>(); queryProps.putAll(wildcardProps); // Extract the wildcarded property keys, ie, where the key is KEY<wildcardEquals> for(Map.Entry<String, String> prop: wildcard.getKeyPropertyList().entrySet()) { if(prop.getKey().endsWith(wc)) { String actualKey = prop.getKey().replaceFirst(wc, ""); wildcardQueryProps.put(actualKey, Pattern.compile(prop.getValue())); queryProps.remove(actualKey); } } // Build the lookup query StringBuilder b = new StringBuilder(wildcard.getDomain()); b.append(":"); // Append the non regex wildcarded properties for(Map.Entry<String, String> qp: queryProps.entrySet()) { b.append(qp.getKey()).append("=").append(qp.getValue()).append(","); } // Append the regex wildcarded property keys and "*" as the value for(String key: wildcardQueryProps.keySet()) { b.append(key).append("=*,"); } // Append a property wild card if the wildcard objectName had: // Property Pattern:true // PropertyList Pattern:true // PropertyValue Pattern:false if(wildcard.isPropertyPattern() && wildcard.isPropertyListPattern() && !wildcard.isPropertyValuePattern()) { b.append("*"); } if(b.toString().endsWith(",")) { b.deleteCharAt(b.length()-1); } // Create the query object try { ObjectName queryObjectName = objectName(b); for(ObjectName qon: conn.queryNames(queryObjectName, null)) { boolean match = true; for(Map.Entry<String, Pattern> pattern: wildcardQueryProps.entrySet()) { match = pattern.getValue().matcher(qon.getKeyProperty(pattern.getKey())).matches(); if(!match) break; } if(match) { names.add(qon); } } } catch (Exception e) { } // Remove all the wildcarded properties from the wildcard objectname's props //ObjectName query = new ObjectName(wildcard.getDomain()); return names; } /* import java.util.regex.*; import javax.management.*; String value = "com.ecs.jms.destinations:service~=[A/B/C],type~=[Queue|Topic],*"; on = new ObjectName(value); println on.getKeyPropertyList(); Pattern p = Pattern.compile("[:|,](\\S+?)~=\\[(\\S+?)\\]"); Matcher m = p.matcher(value); while(m.find()) { println "Group:${m.group(1)}"; println "Group:${m.group(2)}"; } */ // /** // * Returns an attribute map for the MBean with the passed object name registered in the passed server // * @param server The MBeanServer where the target MBean is registered // * @param objectName The object name of the target MBean. Should not be a pattern. For pattern lookups, use {@link JMXHelper#getMBeanAttributeMap(MBeanServerConnection, ObjectName, String, Collection)}. // * @return A map of attribute values keyed by attribute name // */ // public static Map<String, Object> getMBeanAttributeMap(MBeanServerConnection server, ObjectName objectName) { // if(objectName==null) throw new IllegalArgumentException("The passed ObjectName was null", new Throwable()); // if(objectName.isPattern()) throw new IllegalArgumentException("The passed ObjectName was a pattern. For pattern lookups, use {@link JMXHelper#getMBeanAttributeMap(MBeanServerConnection, ObjectName, String, Collection)}", new Throwable()); // if(server==null) server = getHeliosMBeanServer(); // MBean // } /** * Retrieves maps of attribute values keyed by attribute name, in turn keyed by the ObjectName of the MBean. * @param server An MBeanServerConnection * @param objectName An ObjectName which can be absolute or a wildcard. * @param delimeter The delimeter for composite type compound names * @param attributeNames An array of absolute or compound attribute names. * @return a map of results. * TODO: TabularData * TODO: Collections / Maps / Arrays --> ref by index */ public static Map<ObjectName, Map<String, Object>> getMBeanAttributeMap(MBeanServerConnection server, ObjectName objectName, String delimeter, String...attributeNames) { if(server==null) throw new RuntimeException("MBeanServerConnection was null", new Throwable()); if(objectName==null) throw new RuntimeException("ObjectName was null", new Throwable()); if(attributeNames==null || attributeNames.length<1) throw new RuntimeException("Attribute names array was null or zero length", new Throwable()); String[] rootNames = new String[attributeNames.length]; Map<String, String> compoundNames = new HashMap<String, String>(); for(int i = 0; i < attributeNames.length; i++) { String rootKey = null; if(attributeNames[i].contains(delimeter)) { String[] fragments = attributeNames[i].split(Pattern.quote(delimeter)); rootKey = fragments[0]; compoundNames.put(rootKey, attributeNames[i]); } else { rootKey = attributeNames[i]; } rootNames[i] = rootKey; } Map<ObjectName, Map<String, Object>> map = new HashMap<ObjectName, Map<String, Object>>(); try { for(ObjectName on: server.queryNames(objectName, null)) { AttributeList attrs = null; try { attrs = server.getAttributes(on, rootNames); if(attrs.size()<1) continue; } catch (Exception e) { continue; } Map<String, Object> attrMap = new HashMap<String, Object>(); map.put(on, attrMap); for(Attribute attr: attrs.asList()) { Object value = attr.getValue(); if(value==null) continue; String name = attr.getName(); if(value instanceof CompositeData && compoundNames.containsKey(name)) { try { name = compoundNames.get(name); value = extractCompositeData((CompositeData)value, delimeter, name); } catch (Exception e) { continue; } } attrMap.put(name, value); } } } catch (Exception e) { throw new RuntimeException("Failed to acquire attribute names for ObjectName [" + objectName + "] for MBeanServer [" + server + "]", e); } return map; } /** * Retrieves maps of attribute values keyed by attribute name, in turn keyed by the ObjectName of the MBean. * @param server An MBeanServerConnection * @param objectName An ObjectName which can be absolute or a wildcard. * @param delimeter The delimeter for composite type compound names * @param attributeNames An collection of absolute or compound attribute names. * @return a map of results. */ public static Map<ObjectName, Map<String, Object>> getMBeanAttributeMap(MBeanServerConnection server, ObjectName objectName, String delimeter, Collection<String> attributeNames) { if(attributeNames==null || attributeNames.size()<1) throw new RuntimeException("Attribute names collection was null or zero size", new Throwable()); return getMBeanAttributeMap(server, objectName, delimeter, attributeNames.toArray(new String[attributeNames.size()])); } /** * Extracts a composite data field from a CompositeData instance using a compound name. * @param cd The composite data instance * @param delimeter The delimiter used for the compound name * @param name The compound attribute name * @return The extracted object */ public static Object extractCompositeData(final CompositeData cd, final String delimeter, final String name) { String[] fragments = name.split(Pattern.quote(delimeter)); CompositeData ref = cd; Object value = null; for(int i = 1; i < fragments.length; i++) { value = ref.get(fragments[i]); if(value instanceof CompositeData) { ref = (CompositeData)value; } else { break; } } return value; } /** * An regex pattern to parse A[X/Y/Z...] */ public static final Pattern OBJECT_NAME_ATTR_PATTERN = Pattern.compile("(\\S+)\\[(\\S+)\\]"); /** * Retrieves the named attribute from the MBean with the passed ObjectName in the passed MBeanServerConnection. * If the retrieval results in an exception or a null, the default value is returned * @param conn The MBeanServerConnection to the MBeanServer where the target MBean is registered * @param objectName The ObjectName of the target MBean * @param attributeName The attribute name * @param defaultValue The default value * @return The attribute value or the defaut value */ @SuppressWarnings("unchecked") public static <T> T getAttribute(MBeanServerConnection conn, ObjectName objectName, String attributeName, T defaultValue) { try { T t = (T)conn.getAttribute(objectName, attributeName); return t==null ? defaultValue : t; } catch (Exception e) { return defaultValue; } } /** * Retrieves an attribute from an MBeanServer connection. * The compound name is in the format <b><code><ObjectName>[<Fragment<i>1</i>>/<Fragment<i>2</i>>/<Fragment<i>n</i>>]</code></b>. * The multiple fragment names represent support for nested fields in a composite type. * To retrieve a standard "flat" attribute, simply supply one fragment. * @param conn The MBeanServer connection * @param compoundName The compound name * @return the attribute value or null. */ public static Object getAttribute(MBeanServerConnection conn, CharSequence compoundName) { try { Matcher m = OBJECT_NAME_ATTR_PATTERN.matcher(compoundName); if(m.find()) { String objName = m.group(1); String[] fragments = m.group(2).split("/"); return getAttribute(conn, objName, fragments); } return null; } catch (Exception e) { return null; } } /** * Retrieves an attribute from an MBeanServer connection. * @param conn The MBeanServer connection * @param objectName The ObjectName * @param attrs the compound attribute name in the format <b><code><Fragment<i>1</i>>/<Fragment<i>2</i>>/<Fragment<i>n</i>></code></b>. * @return the attribute value or null. */ public static Object getAttribute(MBeanServerConnection conn, String objectName, String...attrs) { return getAttribute(conn, objectName(objectName), attrs); } /** * Retrieves an attribute from an MBeanServer connection with a default compound delimiter of <code>/</code> * @param conn The MBeanServer connection * @param objectName The ObjectName * @param attrs the compound attribute name in the format <b><code><Fragment<i>1</i>>/<Fragment<i>2</i>>/<Fragment<i>n</i>></code></b>. * @return the attribute value or null. */ public static Object getAttribute(MBeanServerConnection conn, ObjectName objectName, String...attrs) { return getAttribute(conn, "/", objectName, attrs); } /** * Retrieves an attribute from an MBeanServer connection. * @param conn The MBeanServer connection * @param delimiter The compund opentype delimiter * @param objectName The ObjectName * @param attrs the compound attribute name in the format <b><code><Fragment<i>1</i>><b>DELIMITER</b><Fragment<i>2</i>><b>DELIMITER</b><Fragment<i>n</i>></code></b>. * @return the attribute value or null. */ public static Object getAttribute(MBeanServerConnection conn, String delimiter, ObjectName objectName, String...attrs) { try { if(objectName!=null && attrs!=null && attrs.length > 0) { ObjectName on = objectName; String key = StringHelper.fastConcatAndDelim(delimiter, attrs); Map<ObjectName, Map<String, Object>> map = getMBeanAttributeMap(conn, on, delimiter, key); return map.get(on).get(key); } } catch (Exception e) { } return null; } /** * Retrieves an attribute from an MBeanServer connection. * @param conn The MBeanServer connection * @param delimiter The compund opentype delimiter * @param objectName The ObjectName * @param attrs the compound attribute name in the format <b><code><Fragment<i>1</i>><b>DELIMITER</b><Fragment<i>2</i>><b>DELIMITER</b><Fragment<i>n</i>></code></b>. * @return the attribute value or null. */ public static Object getAttribute(MBeanServerConnection conn, String delimiter, String objectName, String...attrs) { return getAttribute(conn, delimiter, objectName(objectName), attrs); } /** * Wrapped call to <code>java.beans.Introspector</code>. * Impl. may be swapped out. * @param pojo The object to get the bean info for. * @return A BeanInfo instance. */ public static BeanInfo getBeanInfo(Object pojo) { try { return Introspector.getBeanInfo(pojo.getClass()); } catch (Exception e) { throw new RuntimeException("Failed to create bean info", e); } } /** * Reregisters mbeans from one MBeanServer to another. * @param query An ObjectName mask. * @param source The source MBeanServer * @param target The target MBeanServer * @return The number of MBeans susccessfully re-registered. */ public static int remapMBeans(ObjectName query, MBeanServer source, MBeanServer target) { int remaps = 0; Set<ObjectName> mbeans = target.queryNames(query, null); for(ObjectName on: mbeans) { try { Object proxy = MBeanServerInvocationHandler.newProxyInstance(source, on, DynamicMBean.class, true); target.registerMBean(proxy, on); remaps++; } catch (Exception e) {} } return remaps; } /** * Creates, registers and starts a JMXConnectorServer * @param bindInterface The interface to bind to * @param serviceURL The JMXService URL * @param server The MBeanServer to expose */ public static void fireUpJMXServer(final String bindInterface, final int serverSocketBacklog, CharSequence serviceURL, MBeanServer server) { try { fireUpJMXServer(bindInterface, serverSocketBacklog, new JMXServiceURL(serviceURL.toString()), server); } catch (Exception e) { throw new RuntimeException("Failed to start JMXServer on [" + serviceURL + "]", e); } } /** * Creates, registers and starts a JMXConnectorServer * @param bindInterface The interface to bind to * @param serviceURL The JMXService URL * @param server The MBeanServer to expose */ public static void fireUpJMXServer(final String bindInterface, final int serverSocketBacklog, JMXServiceURL serviceURL, MBeanServer server) { try { Map<String, Object> env = Collections.singletonMap("jmx.remote.rmi.server.socket.factory", (Object)new RMISocketFactory(){ public ServerSocket createServerSocket(int port) throws IOException { return new ServerSocket(port, serverSocketBacklog, InetAddress.getByName(bindInterface)); } public Socket createSocket(String host, int port) throws IOException { return new Socket(host, port); } }); JMXConnectorServer jmxServer = JMXConnectorServerFactory.newJMXConnectorServer(serviceURL, env, server); server.registerMBean(jmxServer, JMXHelper.objectName("org.helios.netty:service=JMXConnectorServer,url=" + ObjectName.quote(serviceURL.toString()))); jmxServer.start(); } catch (Exception e) { throw new RuntimeException("Failed to start JMXServer on [" + serviceURL + "]", e); } } public static void fireUpRMIRegistry(final String bindInterface, final int port) { try { LocateRegistry.createRegistry(port); } catch (Exception e) { throw new RuntimeException("Failed to start RMIRegistry on [" + bindInterface + ":" + port + "]", e); } } } class NVP { String name = null; Object value = null; public static Collection<NVP> generate(Object...args) { List<NVP> list = new ArrayList<NVP>(args.length); String name = null; for(int i=0; i<args.length; i++) { if(i+1 < args.length) { name=args[i].toString(); i++; list.add(new NVP(name, args[i])); } } return list; } /** * @param name The NVP name * @param value The NVP value */ public NVP(String name, Object value) { super(); this.name = name; this.value = value; } /** * @return the name */ public String getName() { return name; } /** * @param name the name to set */ public void setName(String name) { this.name = name; } /** * @return the value */ public Object getValue() { return value; } /** * @param value the value to set */ public void setValue(Object value) { this.value = value; } }