package com.revolsys.jmx; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import javax.management.AttributeNotFoundException; import javax.management.InstanceNotFoundException; import javax.management.MBeanInfo; import javax.management.MBeanOperationInfo; import javax.management.MBeanParameterInfo; import javax.management.MBeanServerConnection; import javax.management.MalformedObjectNameException; import javax.management.ObjectName; import com.revolsys.io.map.MapWriter; import com.revolsys.logging.Logs; import com.revolsys.record.schema.FieldDefinition; import com.revolsys.util.Exceptions; /** * <p> * The JmxService provides a wrapper around JMX connections to one or more * servers. The service is configured with a list of server definitions which * includes a logical name for the server and a connection to the server. Each * server has a list of object names and labels and the list of attributes and * operations to be exposed on that server. The only servers, objects, * attributes and operations to be exposed are those in the configuration. * * @author Paul Austin paul.austin@revolsys.com */ public class JmxService { /** The JMX server configurations. */ private final Map<String, Map<String, Object>> jmxServers = new LinkedHashMap<>(); /** * Construct a new JmxService. * * @param jmxServers The list of JMX server configurations. */ public JmxService(final List<Map<String, Object>> jmxServers) { for (final Map<String, Object> server : jmxServers) { final String name = (String)server.get("name"); if (name == null) { Logs.error(this, "Missing name for JMX Server: " + server); } this.jmxServers.put(name, server); } } /** * Get the value for the attribute for an object on a server. * * @param mapWriter The writer to write the attributes to. * @param serverId The name of the server. * @param objectId The name of the object. * @param attributeId The name of the attribute. * @return The attribute value. */ public Object getAttribute(final String serverId, final String objectId, final String attributeId) { if (hasAttribute(serverId, objectId, attributeId)) { final MBeanServerConnection connection = getConnection(serverId); try { final ObjectName objectName = getObjectName(serverId, objectId); final Object object = connection.getAttribute(objectName, attributeId); return object; } catch (final InstanceNotFoundException e) { return "Unavailable"; } catch (final MalformedObjectNameException e) { throw new IllegalArgumentException("MBean name not valid " + serverId + " " + objectId); } catch (final AttributeNotFoundException e) { throw new IllegalArgumentException( "MBean attribute not found " + serverId + " " + objectId + "." + attributeId); } catch (final Throwable e) { return Exceptions.throwUncheckedException(e); } } else { throw new IllegalArgumentException( "Attribute not configured " + serverId + " " + objectId + "." + attributeId); } } /** * Get all the attributes names for an object on a server. * * @param serverId The name of the server. * @param objectId The name of the object. * @return The attribute names. */ @SuppressWarnings("unchecked") public List<String> getAttributeNames(final String serverId, final String objectId) { final Map<String, Object> object = getObjectParams(serverId, objectId); final List<Map<String, Object>> attributes = (List<Map<String, Object>>)object .get("attributes"); final List<String> fieldNames = new ArrayList<>(); for (final Map<String, Object> field : attributes) { final String name = (String)field.get("attributeName"); if (name != null) { fieldNames.add(name); } } return fieldNames; } /** * Get the values for all attributes for an object on a server. * * @param serverId The name of the server. * @param objectId The name of the object. * @return The attribute values. */ public List<FieldDefinition> getAttributes(final String serverId, final String objectId) { final MBeanServerConnection connection = getConnection(serverId); try { final String[] attributeNames = getAttributeNames(serverId, objectId).toArray(new String[0]); final ObjectName objectName = getObjectName(serverId, objectId); final List<FieldDefinition> attributeValues = new ArrayList<>(); for (final Object attribute : connection.getAttributes(objectName, attributeNames)) { attributeValues.add((FieldDefinition)attribute); } return attributeValues; } catch (final InstanceNotFoundException e) { throw new IllegalArgumentException("MBean not found " + serverId + " " + objectId); } catch (final MalformedObjectNameException e) { throw new IllegalArgumentException("MBean name not valid " + serverId + " " + objectId); } catch (final Throwable e) { return Exceptions.throwUncheckedException(e); } } /** * Get a connection to the specified serverId. Returns an * {@link IllegalArgumentException} if the host or connection could not be * found. * * @param serverId The name of the server. * @return The JMX connection. */ private MBeanServerConnection getConnection(final String serverId) { final Map<String, Object> server = getServerParams(serverId); final MBeanServerConnection connection = (MBeanServerConnection)server.get("connection"); if (connection == null) { throw new IllegalArgumentException("Connection for server " + serverId + " not found"); } else { return connection; } } /** * Get the object name for object on a server. Throws an * IllegalArgumentException if the object does not have a configuration. * * @param serverId The name of the server. * @param objectId The name of the object. * @return The server parameters. * @throws MalformedObjectNameException If the name of the object was not * valid. */ private ObjectName getObjectName(final String serverId, final String objectId) throws MalformedObjectNameException { final Map<String, Object> object = getObjectParams(serverId, objectId); final String objectName = (String)object.get("objectName"); return new ObjectName(objectName); } /** * Get the configuration parameters for all objects on a server. Throws an * IllegalArgumentException if there are no objects configured. * * @param serverId The name of the server. * @return The server parameters. */ @SuppressWarnings("unchecked") private List<Map<String, Object>> getObjectParams(final String serverId) { final Map<String, Object> server = getServerParams(serverId); final List<Map<String, Object>> objects = (List<Map<String, Object>>)server.get("objects"); if (objects == null) { throw new IllegalArgumentException( "Server " + serverId + " does not have an objects attribute"); } return objects; } /** * Get the configuration parameters for an object on a server. Throws an * IllegalArgumentException if the object does not have a configuration. * * @param serverId The name of the server. * @param objectId The name of the object. * @return The server parameters. */ private Map<String, Object> getObjectParams(final String serverId, final String objectId) { final List<Map<String, Object>> objectNames = getObjectParams(serverId); for (final Map<String, Object> object : objectNames) { final String objectLabel = (String)object.get("objectLabel"); if (objectId.equals(objectLabel)) { return object; } } throw new IllegalArgumentException( "Object for server " + serverId + " " + objectId + " not found"); } /** * Get the description for the operation for an object on a server. * * @param serverId The name of the server. * @param objectId The name of the object. * @param operationId The name of the operation. * @return The operations values. */ public MBeanOperationInfo getOperation(final String serverId, final String objectId, final String operationId) { if (hasOperation(serverId, objectId, operationId)) { final MBeanServerConnection connection = getConnection(serverId); try { final ObjectName objectName = getObjectName(serverId, objectId); final MBeanInfo beanInfo = connection.getMBeanInfo(objectName); for (final MBeanOperationInfo operation : beanInfo.getOperations()) { final String name = operation.getName(); if (operationId.equals(name)) { return operation; } } throw new IllegalArgumentException( "MBean Operation not found " + serverId + " " + objectId + "." + operationId); } catch (final InstanceNotFoundException e) { throw new IllegalArgumentException("MBean not found " + serverId + " " + objectId); } catch (final MalformedObjectNameException e) { throw new IllegalArgumentException("MBean name not valid " + serverId + " " + objectId); } catch (final Throwable e) { return Exceptions.throwUncheckedException(e); } } else { throw new IllegalArgumentException( "Operation not configured " + serverId + " " + objectId + "." + operationId); } } /** * Get all the operations names for an object on a server. * * @param serverId The name of the server. * @param objectId The name of the object. * @return The attribute names. */ @SuppressWarnings("unchecked") public List<String> getOperationNames(final String serverId, final String objectId) { final Map<String, Object> object = getObjectParams(serverId, objectId); final List<Map<String, Object>> operations = (List<Map<String, Object>>)object .get("operations"); if (operations == null) { throw new IllegalArgumentException( "MBean " + serverId + " " + objectId + " does not have any operations"); } else { final List<String> operationNames = new ArrayList<>(); for (final Map<String, Object> attribute : operations) { final String name = (String)attribute.get("operationName"); if (name != null) { operationNames.add(name); } } return operationNames; } } /** * Get the description for all operations for an object on a server. * * @param serverId The name of the server. * @param objectId The name of the object. * @return The operations values. */ public List<MBeanOperationInfo> getOperations(final String serverId, final String objectId) { final MBeanServerConnection connection = getConnection(serverId); try { final ObjectName objectName = getObjectName(serverId, objectId); final List<String> operationNames = getOperationNames(serverId, objectId); final MBeanInfo beanInfo = connection.getMBeanInfo(objectName); final List<MBeanOperationInfo> operations = new ArrayList<>(); for (final MBeanOperationInfo operation : beanInfo.getOperations()) { final String name = operation.getName(); if (operationNames.contains(name)) { operations.add(operation); } } return operations; } catch (final InstanceNotFoundException e) { throw new IllegalArgumentException("MBean not found " + serverId + " " + objectId); } catch (final MalformedObjectNameException e) { throw new IllegalArgumentException("MBean name not valid " + serverId + " " + objectId); } catch (final Throwable e) { return Exceptions.throwUncheckedException(e); } } /** * Get the configuration parameters for a server. Throws an * IllegalArgumentException if the server does not have a configuration. * * @param serverId The name of the server. * @return The server parameters. */ private Map<String, Object> getServerParams(final String serverId) { final Map<String, Object> server = this.jmxServers.get(serverId); if (server == null) { throw new IllegalArgumentException("Server " + serverId + " not found"); } else { return server; } } /** * Check to see if the object has been configured to have the specified * attribute. * * @param serverId The name of the server. * @param objectId The name of the object. * @param attributeId The name of the attribute. * @return True if the attribute exists. */ public boolean hasAttribute(final String serverId, final String objectId, final String attributeId) { return getAttributeNames(serverId, objectId).contains(attributeId); } /** * Check to see if the object has been configured to have the specified * operation. * * @param serverId The name of the server. * @param objectId The name of the object. * @param operationId The name of the operation. * @return True if the operation exists. */ public boolean hasOperation(final String serverId, final String objectId, final String operationId) { return getOperationNames(serverId, objectId).contains(operationId); } /** * Invoke the operation with the specified parameters. * * @param serverId The name of the server. * @param objectId The name of the object. * @param operationId The name of the operation. * @param parameters The parameters to pass to the operation. * @return The result of executing the operation. */ public Object invokeOperation(final String serverId, final String objectId, final String operationId, final Map<String, String> parameters) { try { final MBeanOperationInfo operation = getOperation(serverId, objectId, operationId); final MBeanServerConnection connection = getConnection(serverId); final ObjectName objectName = getObjectName(serverId, objectId); final MBeanParameterInfo[] parameterInfos = operation.getSignature(); final String[] signature = new String[parameterInfos.length]; final Object[] values = new Object[parameterInfos.length]; for (int i = 0; i < parameterInfos.length; i++) { final MBeanParameterInfo parameterInfo = parameterInfos[i]; final String name = parameterInfo.getName(); final String type = parameterInfo.getType(); signature[i] = type; values[i] = parameters.get(name); } return connection.invoke(objectName, operation.getName(), values, signature); } catch (final InstanceNotFoundException e) { throw new IllegalArgumentException("MBean not found " + serverId + " " + objectId); } catch (final MalformedObjectNameException e) { throw new IllegalArgumentException("MBean name not valid " + serverId + " " + objectId); } catch (final Throwable e) { return Exceptions.throwUncheckedException(e); } } /** * Write the attribute for the attribute for an object on a server. * * @param mapWriter The writer to write the attributes to. * @param serverId The name of the server. * @param objectId The name of the object. * @param attributeId The name of the attribute. */ public void writeAttribute(final MapWriter mapWriter, final String serverId, final String objectId, final String attributeId) { Object value; try { value = getAttribute(serverId, objectId, attributeId); } catch (final Throwable e) { value = e.getMessage(); } writeAttribute(mapWriter, serverId, objectId, attributeId, value); } /** * Write the attribute for the attribute for an object on a server. * * @param mapWriter The writer to write the attributes to. * @param serverId The name of the server. * @param objectId The name of the object. * @param attributeId The name of the attribute. * @param value The value to write. */ public void writeAttribute(final MapWriter mapWriter, final String serverId, final String objectId, final String attributeId, final Object value) { final Map<String, Object> attributeMap = new LinkedHashMap<>(); attributeMap.put("serverId", serverId); attributeMap.put("objectId", objectId); attributeMap.put("attributeId", attributeId); attributeMap.put("value", value); mapWriter.write(attributeMap); } /** * Write the attributes for all servers and objects. * * @param mapWriter The writer to write the attributes to. */ public void writeAttributes(final MapWriter mapWriter) { for (final String serverId : this.jmxServers.keySet()) { writeAttributes(mapWriter, serverId); } } /** * Write the attributes for all objects for a server. * * @param mapWriter The writer to write the attributes to. * @param serverId The name of the server. */ public void writeAttributes(final MapWriter mapWriter, final String serverId) { final List<Map<String, Object>> objects = getObjectParams(serverId); for (final Map<String, Object> object : objects) { final String objectId = (String)object.get("objectLabel"); writeAttributes(mapWriter, serverId, objectId); } } /** * Write the attributes for the objects on a server. * * @param mapWriter The writer to write the attributes to. * @param serverId The name of the server. * @param objectId The name of the object. */ public void writeAttributes(final MapWriter mapWriter, final String serverId, final String objectId) { final String[] attributeNames = getAttributeNames(serverId, objectId).toArray(new String[0]); for (final String attributeId : attributeNames) { writeAttribute(mapWriter, serverId, objectId, attributeId); } } /** * Write the operation for the operation for an object on a server. * * @param mapWriter The writer to write the operation to. * @param serverId The name of the server. * @param objectId The name of the object. * @param operation The operation details. * @param returnValue The value returned from invoking the method. */ public void writeOperation(final MapWriter mapWriter, final String serverId, final String objectId, final MBeanOperationInfo operation, final Object returnValue) { final Map<String, Object> attributeMap = new LinkedHashMap<>(); attributeMap.put("serverId", serverId); attributeMap.put("objectId", objectId); attributeMap.put("operationId", operation.getName()); attributeMap.put("returnType", operation.getReturnType()); final MBeanParameterInfo[] parameters = operation.getSignature(); final StringBuilder parameterSpec = new StringBuilder(); if (parameters.length > 0) { for (final MBeanParameterInfo parameter : parameters) { final String name = parameter.getName(); final String type = parameter.getType(); parameterSpec.append(name).append(':').append(type).append(','); } parameterSpec.setLength(parameterSpec.length() - 1); } attributeMap.put("parameters", parameterSpec); attributeMap.put("returnValue", returnValue); mapWriter.write(attributeMap); } /** * Write the operation for the operation for an object on a server. * * @param mapWriter The writer to write the operation to. * @param serverId The name of the server. * @param objectId The name of the object. * @param operationId The name of the operations. */ public void writeOperation(final MapWriter mapWriter, final String serverId, final String objectId, final String operationId) { final MBeanOperationInfo operation = getOperation(serverId, objectId, operationId); writeOperation(mapWriter, serverId, objectId, operation, null); } /** * Write the operation for the operation for an object on a server. * * @param mapWriter The writer to write the operation to. * @param serverId The name of the server. * @param objectId The name of the object. * @param operationId The name of the operations. * @param returnValue The value returned from invoking the method. */ public void writeOperation(final MapWriter mapWriter, final String serverId, final String objectId, final String operationId, final Object returnValue) { final MBeanOperationInfo operation = getOperation(serverId, objectId, operationId); writeOperation(mapWriter, serverId, objectId, operation, returnValue); } /** * Write the operations for all servers and objects. * * @param mapWriter The writer to write the operations to. */ public void writeOperations(final MapWriter mapWriter) { for (final String serverId : this.jmxServers.keySet()) { writeOperations(mapWriter, serverId); } } /** * Write the operations for all objects for a server. * * @param mapWriter The writer to write the operations to. * @param serverId The name of the server. */ public void writeOperations(final MapWriter mapWriter, final String serverId) { final List<Map<String, Object>> objects = getObjectParams(serverId); for (final Map<String, Object> object : objects) { final String objectId = (String)object.get("objectLabel"); try { writeOperations(mapWriter, serverId, objectId); } catch (final IllegalArgumentException e) { } } } /** * Write the operations for the objects on a server. * * @param mapWriter The writer to write the operations to. * @param serverId The name of the server. * @param objectId The name of the object. */ public void writeOperations(final MapWriter mapWriter, final String serverId, final String objectId) { final List<MBeanOperationInfo> operations = getOperations(serverId, objectId); for (final MBeanOperationInfo operation : operations) { writeOperation(mapWriter, serverId, objectId, operation, null); } } }