/* WebMBeanAdapter.java * * Copyright 2009-2015 Comcast Interactive Media, LLC. * * 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 org.fishwife.jrugged.spring.jmx; import javax.management.Attribute; import javax.management.AttributeList; import javax.management.AttributeNotFoundException; import javax.management.InstanceNotFoundException; import javax.management.JMException; import javax.management.MBeanAttributeInfo; import javax.management.MBeanInfo; import javax.management.MBeanOperationInfo; import javax.management.MBeanServer; import javax.management.MalformedObjectNameException; import javax.management.ObjectName; import javax.management.ReflectionException; import java.io.UnsupportedEncodingException; import java.util.HashSet; import java.util.Map; import java.util.TreeMap; /** * The WebMBeanAdapter is used to query MBean Attributes and Operations using * web-friendly names. * * It should be noted that by creating a web interface the JMX beans bypasses the JMX security * mechanisms that are built into the JVM. If there is a need to limit access to the JMX * beans then the web interface will need to be secured. */ public class WebMBeanAdapter { private MBeanServer mBeanServer; private ObjectName objectName; private MBeanStringSanitizer sanitizer; private String encoding; private MBeanInfo mBeanInfo; /** * Constructor. * @param mBeanServer the {@link MBeanServer}. * @param mBeanName the MBean name (can be URL-encoded). * @param encoding the string encoding to be used (i.e. UTF-8) * @throws JMException Java Management Exception * @throws UnsupportedEncodingException if the encoding is not supported. */ public WebMBeanAdapter(MBeanServer mBeanServer, String mBeanName, String encoding) throws JMException, UnsupportedEncodingException { this.mBeanServer = mBeanServer; this.encoding = encoding; sanitizer = createMBeanStringSanitizer(); objectName = createObjectName(sanitizer.urlDecode(mBeanName, encoding)); mBeanInfo = mBeanServer.getMBeanInfo(objectName); } /** * Get the Attribute metadata for an MBean by name. * @return the {@link Map} of {@link String} attribute names to {@link MBeanAttributeInfo} values. */ public Map<String, MBeanAttributeInfo> getAttributeMetadata() { MBeanAttributeInfo[] attributeList = mBeanInfo.getAttributes(); Map<String, MBeanAttributeInfo> attributeMap = new TreeMap<String, MBeanAttributeInfo>(); for (MBeanAttributeInfo attribute: attributeList) { attributeMap.put(attribute.getName(), attribute); } return attributeMap; } /** * Get the Operation metadata for an MBean by name. * @return the {@link Map} of {@link String} operation names to {@link MBeanOperationInfo} values. */ public Map<String, MBeanOperationInfo> getOperationMetadata() { MBeanOperationInfo[] operations = mBeanInfo.getOperations(); Map<String, MBeanOperationInfo> operationMap = new TreeMap<String, MBeanOperationInfo>(); for (MBeanOperationInfo operation: operations) { operationMap.put(operation.getName(), operation); } return operationMap; } /** * Get the Operation metadata for a single operation on an MBean by name. * @param operationName the Operation name (can be URL-encoded). * @return the {@link MBeanOperationInfo} for the operation. * @throws OperationNotFoundException Method was not found * @throws UnsupportedEncodingException if the encoding is not supported. */ public MBeanOperationInfo getOperationInfo(String operationName) throws OperationNotFoundException, UnsupportedEncodingException { String decodedOperationName = sanitizer.urlDecode(operationName, encoding); Map<String, MBeanOperationInfo> operationMap = getOperationMetadata(); if (operationMap.containsKey(decodedOperationName)) { return operationMap.get(decodedOperationName); } throw new OperationNotFoundException("Could not find operation " + operationName + " on MBean " + objectName.getCanonicalName()); } /** * Get all the attribute values for an MBean by name. The values are HTML escaped. * @return the {@link Map} of attribute names and values. * @throws javax.management.AttributeNotFoundException Unable to find the 'attribute' * @throws InstanceNotFoundException unable to find the specific bean * @throws ReflectionException unable to interrogate the bean */ public Map<String,Object> getAttributeValues() throws AttributeNotFoundException, InstanceNotFoundException, ReflectionException { HashSet<String> attributeSet = new HashSet<String>(); for (MBeanAttributeInfo attributeInfo : mBeanInfo.getAttributes()) { attributeSet.add(attributeInfo.getName()); } AttributeList attributeList = mBeanServer.getAttributes(objectName, attributeSet.toArray(new String[attributeSet.size()])); Map<String, Object> attributeValueMap = new TreeMap<String, Object>(); for (Attribute attribute : attributeList.asList()) { attributeValueMap.put(attribute.getName(), sanitizer.escapeValue(attribute.getValue())); } return attributeValueMap; } /** * Get the value for a single attribute on an MBean by name. * @param attributeName the attribute name (can be URL-encoded). * @return the value as a String. * @throws JMException Java Management Exception * @throws UnsupportedEncodingException if the encoding is not supported. */ public String getAttributeValue(String attributeName) throws JMException, UnsupportedEncodingException { String decodedAttributeName = sanitizer.urlDecode(attributeName, encoding); return sanitizer.escapeValue(mBeanServer.getAttribute(objectName, decodedAttributeName)); } /** * Invoke an operation on an MBean by name. * Note that only basic data types are supported for parameter values. * @param operationName the operation name (can be URL-encoded). * @param parameterMap the {@link Map} of parameter names and value arrays. * @return the returned value from the operation. * @throws JMException Java Management Exception * @throws UnsupportedEncodingException if the encoding is not supported. */ public String invokeOperation(String operationName, Map<String, String[]> parameterMap) throws JMException, UnsupportedEncodingException { MBeanOperationInfo operationInfo = getOperationInfo(operationName); MBeanOperationInvoker invoker = createMBeanOperationInvoker(mBeanServer, objectName, operationInfo); return sanitizer.escapeValue(invoker.invokeOperation(parameterMap)); } MBeanStringSanitizer createMBeanStringSanitizer() { return new MBeanStringSanitizer(); } ObjectName createObjectName(String name) throws MalformedObjectNameException { return new ObjectName(name); } MBeanOperationInvoker createMBeanOperationInvoker( MBeanServer mBeanServer, ObjectName objectName, MBeanOperationInfo operationInfo) { return new MBeanOperationInvoker(mBeanServer, objectName, operationInfo); } }